>(rooms);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Helpdesk/src/GalaxyHotel.HelpDesk/Views/Shared/Error.cshtml:
--------------------------------------------------------------------------------
1 | @model ErrorViewModel
2 | @{
3 | ViewData["Title"] = "Error";
4 | }
5 |
6 | Error.
7 | An error occurred while processing your request.
8 |
9 | @if (Model.ShowRequestId)
10 | {
11 |
12 | Request ID: @Model.RequestId
13 |
14 | }
15 |
16 | Development Mode
17 |
18 | Swapping to Development environment will display more detailed information about the error that occurred.
19 |
20 |
21 | The Development environment shouldn't be enabled for deployed applications.
22 | It can result in displaying sensitive information from exceptions to end users.
23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
24 | and restarting the app.
25 |
26 |
--------------------------------------------------------------------------------
/Website/src/GalaxyHotel.Website/Views/Shared/Error.cshtml:
--------------------------------------------------------------------------------
1 | @model ErrorViewModel
2 | @{
3 | ViewData["Title"] = "Error";
4 | }
5 |
6 | Error.
7 | An error occurred while processing your request.
8 |
9 | @if (Model.ShowRequestId)
10 | {
11 |
12 | Request ID: @Model.RequestId
13 |
14 | }
15 |
16 | Development Mode
17 |
18 | Swapping to Development environment will display more detailed information about the error that occurred.
19 |
20 |
21 | The Development environment shouldn't be enabled for deployed applications.
22 | It can result in displaying sensitive information from exceptions to end users.
23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
24 | and restarting the app.
25 |
26 |
--------------------------------------------------------------------------------
/Libraries/src/GalaxyHotel.Core/Mapping/GalaxyHotelMapping.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using GalaxyHotel.Core.Models;
3 | using GalaxyHotel.Dto;
4 |
5 | namespace GalaxyHotel.Core.Mapping
6 | {
7 | public class GalaxyHotelProfile : Profile
8 | {
9 | public GalaxyHotelProfile()
10 | {
11 | CreateMap()
12 | .ForMember(dest => dest.RoomName, opts => opts.MapFrom(src => src.Room.Name))
13 | .ForMember(dest => dest.GuestName, opts => opts.MapFrom(src => $"{src.Guest.FirstName} {src.Guest.LastName}"));
14 |
15 | CreateMap()
16 | .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.Id))
17 | .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name))
18 | .ForMember(dest => dest.Floor, opts => opts.MapFrom(src => src.Floor))
19 | .ForMember(dest => dest.PricePerNight, opts => opts.MapFrom(src => src.PricePerNight));
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Libraries/src/GalaxyHotel.Core/Entities/RoomAggregate/Room.cs:
--------------------------------------------------------------------------------
1 | using Ardalis.GuardClauses;
2 | using GalaxyHotel.Core.Interfaces;
3 |
4 | namespace GalaxyHotel.Core.Entities.RoomAggregate
5 | {
6 | public class Room : BaseEntity, IAggregateRoot
7 | {
8 | public string Name { get; private set; }
9 | public int Floor { get; private set; }
10 | public int RoomTypeId { get; private set; }
11 | public decimal PricePerNight { get; private set; }
12 |
13 | public Room() { }
14 |
15 | public Room(string name, int floor, int roomTypeId, decimal pricePerNight)
16 | {
17 | Guard.Against.NullOrEmpty(name, nameof(name));
18 | Guard.Against.NegativeOrZero(floor, nameof(floor));
19 | Guard.Against.NegativeOrZero(roomTypeId, nameof(roomTypeId));
20 | Guard.Against.NegativeOrZero(pricePerNight, nameof(pricePerNight));
21 |
22 | Name = name;
23 | Floor = floor;
24 | RoomTypeId = roomTypeId;
25 | PricePerNight = pricePerNight;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Website/tests/GalaxyHotel.UITests/GalaxyHotel.UITests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Website/src/GalaxyHotel.Website/Views/Shared/_LoginPartial.cshtml:
--------------------------------------------------------------------------------
1 | @using Microsoft.AspNetCore.Identity
2 | @inject SignInManager SignInManager
3 | @inject UserManager UserManager
4 |
5 |
27 |
--------------------------------------------------------------------------------
/Helpdesk/src/GalaxyHotel.HelpDesk/Views/Home/Reservations.cshtml:
--------------------------------------------------------------------------------
1 | @model ReservationViewModel
2 |
3 | @{
4 | ViewData["Title"] = "Home Page";
5 | }
6 |
7 |
8 |
Reservations
9 |
10 |
11 |
12 | Check In
13 | Check Out
14 | Guest
15 | Room
16 | Price
17 |
18 |
19 |
20 | @foreach (var reservation in Model.Reservations)
21 | {
22 |
23 | @reservation.CheckIn.ToString("d")
24 | @reservation.CheckOut.ToString("d")
25 | @reservation.GuestName
26 | @reservation.RoomName
27 | @reservation.TotalPrice
28 | @Html.ActionLink("Cancel", "Cancel")
29 |
30 | }
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Syncfusion Succinctly E-Books
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Libraries/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | trigger:
2 | - develop
3 | - master
4 |
5 | variables:
6 | buildConfiguration: 'Release'
7 |
8 | steps:
9 | - task: gitversion/setup@0
10 | displayName: Install GitVersion
11 | inputs:
12 | versionSpec: '5.x'
13 |
14 | - task: gitversion/execute@0
15 | displayName: Generate a new version number
16 | inputs:
17 | useConfigFile: true
18 | configFilePath: 'GitVersion.yml'
19 |
20 | - task: DotNetCoreCLI@2
21 | displayName: Build libraries
22 | inputs:
23 | command: 'build'
24 |
25 | - task: DotNetCoreCLI@2
26 | displayName: Test libraries
27 | inputs:
28 | command: 'test'
29 |
30 | - task: DotNetCoreCLI@2
31 | displayName: Pack libraries
32 | inputs:
33 | command: 'pack'
34 | packagesToPack: '**/*.csproj'
35 | versioningScheme: 'byEnvVar'
36 | versionEnvVar: 'GitVersion.NugetVersion'
37 |
38 | - task: DotNetCoreCLI@2
39 | displayName: Push libraries
40 | inputs:
41 | command: 'push'
42 | packagesToPush: '$(Build.ArtifactStagingDirectory)/*.nupkg'
43 | nuGetFeedType: 'internal'
44 | publishVstsFeed: 'cd43281b-c764-45ca-a804-974d979b8d0b/b4b93fc7-3bd6-45a6-a7bb-2c07e846463e'
45 |
--------------------------------------------------------------------------------
/Libraries/src/GalaxyHotel.Core/Entities/GuestAggregate/Guest.cs:
--------------------------------------------------------------------------------
1 | using Ardalis.GuardClauses;
2 | using GalaxyHotel.Core.Interfaces;
3 | using System.Collections.Generic;
4 |
5 | namespace GalaxyHotel.Core.Entities.GuestAggregate
6 | {
7 | public class Guest : BaseEntity, IAggregateRoot
8 | {
9 | private List paymentMethods = new List();
10 | public IEnumerable PaymentMethods => paymentMethods.AsReadOnly();
11 |
12 | public string FirstName { get; private set; }
13 | public string LastName { get; private set; }
14 | public Address Address { get; private set; }
15 |
16 | public Guest()
17 | {
18 |
19 | }
20 |
21 | public Guest(string firstName, string lastName, Address address)
22 | {
23 | Guard.Against.NullOrEmpty(firstName, nameof(firstName));
24 | Guard.Against.NullOrEmpty(lastName, nameof(lastName));
25 | Guard.Against.Null(address, nameof(address));
26 |
27 | this.FirstName = firstName;
28 | this.LastName = lastName;
29 | this.Address = address;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Website/src/GalaxyHotel.Website/wwwroot/lib/jquery-validation/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | =====================
3 |
4 | Copyright Jörn Zaefferer
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/Helpdesk/src/GalaxyHotel.HelpDesk/wwwroot/lib/jquery-validation/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | =====================
3 |
4 | Copyright Jörn Zaefferer
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/Website/build-website.yml:
--------------------------------------------------------------------------------
1 | parameters:
2 | - name: librariesFeed
3 | displayName: 'Restore from Libraries NuGet feed'
4 | type: boolean
5 | default: true
6 | - name: enablePublish
7 | displayName: 'Enable publish'
8 | type: boolean
9 | default: true
10 | - name: publishArgs
11 | displayName: 'Publish Arguments'
12 | type: string
13 | default: ''
14 |
15 | steps:
16 | - ${{ if eq(parameters.librariesFeed, true) }}:
17 | - task: DotNetCoreCLI@2
18 | displayName: Restore packages
19 | inputs:
20 | command: 'restore'
21 | feedsToUse: 'select'
22 | vstsFeed: '834230a0-50c2-4971-954a-75eb9e3d47dd/dcb14a3c-e6ab-48df-ac52-3ae79a5055f7'
23 |
24 | - task: DotNetCoreCLI@2
25 | displayName: Build the website
26 | inputs:
27 | command: 'build'
28 | projects: '**/*.csproj'
29 |
30 | - task: DotNetCoreCLI@2
31 | displayName: Run tests
32 | inputs:
33 | command: 'test'
34 | projects: 'tests/**/*.csproj;!tests/**/*UITests*.csproj'
35 |
36 | - ${{ if eq(parameters.enablePublish, true) }}:
37 | - task: DotNetCoreCLI@2
38 | displayName: Publish the website
39 | inputs:
40 | command: 'publish'
41 | publishWebProjects: true
42 | arguments: ${{ parameters.publishArgs }}
--------------------------------------------------------------------------------
/Website/src/GalaxyHotel.Website/wwwroot/lib/bootstrap/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2011-2018 Twitter, Inc.
4 | Copyright (c) 2011-2018 The Bootstrap Authors
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/Helpdesk/src/GalaxyHotel.HelpDesk/wwwroot/lib/bootstrap/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2011-2018 Twitter, Inc.
4 | Copyright (c) 2011-2018 The Bootstrap Authors
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/Website/tests/GalaxyHotel.UITests/BookingTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 | using OpenQA.Selenium;
3 | using OpenQA.Selenium.Chrome;
4 |
5 | namespace GalaxyHotel.UITests
6 | {
7 | [TestClass]
8 | public class BookingTests
9 | {
10 | private IWebDriver webDriver;
11 | private string baseUrl;
12 |
13 | public TestContext TestContext { get; set; }
14 |
15 | [TestMethod]
16 | [TestCategory("Chrome")]
17 | public void BookARoomAndCheckForConfirmationTest()
18 | {
19 | webDriver.Navigate().GoToUrl(baseUrl);
20 | webDriver.FindElement(By.LinkText("Book")).Click();
21 |
22 | var bookingConfirmationText = webDriver.FindElement(By.XPath("//h1")).Text;
23 |
24 | Assert.AreEqual("Room booked!", bookingConfirmationText);
25 | }
26 |
27 | [TestInitialize()]
28 | public void SetupTest()
29 | {
30 | baseUrl = TestContext.Properties["baseUrl"].ToString();
31 | webDriver = new ChromeDriver();
32 | }
33 |
34 | [TestCleanup]
35 | public void TeardownTest()
36 | {
37 | webDriver.Quit();
38 | webDriver.Dispose();
39 | webDriver = null;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Website/src/GalaxyHotel.Website/GalaxyHotel.Website.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | aspnet-GalaxyHotel.Website-F4CD36CD-E24A-49DF-B7F9-29E514408D94
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | all
20 | runtime; build; native; contentfiles; analyzers; buildtransitive
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Helpdesk/GalaxyHotel.HelpDesk.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30204.135
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GalaxyHotel.HelpDesk", "src\GalaxyHotel.HelpDesk\GalaxyHotel.HelpDesk.csproj", "{AD5E5C66-3AE0-4691-B4AF-9FDD8BAE6350}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{971F35C2-5B69-4D7B-9454-10D5C50B41C2}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {AD5E5C66-3AE0-4691-B4AF-9FDD8BAE6350}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {AD5E5C66-3AE0-4691-B4AF-9FDD8BAE6350}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {AD5E5C66-3AE0-4691-B4AF-9FDD8BAE6350}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {AD5E5C66-3AE0-4691-B4AF-9FDD8BAE6350}.Release|Any CPU.Build.0 = Release|Any CPU
20 | EndGlobalSection
21 | GlobalSection(SolutionProperties) = preSolution
22 | HideSolutionNode = FALSE
23 | EndGlobalSection
24 | GlobalSection(NestedProjects) = preSolution
25 | {AD5E5C66-3AE0-4691-B4AF-9FDD8BAE6350} = {971F35C2-5B69-4D7B-9454-10D5C50B41C2}
26 | EndGlobalSection
27 | GlobalSection(ExtensibilityGlobals) = postSolution
28 | SolutionGuid = {C85C9E2D-4FC7-4457-B37B-69F3A3CE0209}
29 | EndGlobalSection
30 | EndGlobal
31 |
--------------------------------------------------------------------------------
/Libraries/src/GalaxyHotel.Infrastructure/Data/SpecificationEvaluator.cs:
--------------------------------------------------------------------------------
1 | using GalaxyHotel.Core.Interfaces;
2 | using GalaxyHotel.Core.Models;
3 | using Microsoft.EntityFrameworkCore;
4 | using System.Linq;
5 |
6 | namespace GalaxyHotel.Infrastructure.Data
7 | {
8 | public class SpecificationEvaluator where T : BaseEntity
9 | {
10 | public static IQueryable GetQuery(IQueryable inputQuery, ISpecification specification)
11 | {
12 | var query = inputQuery;
13 |
14 | if (specification.Criteria != null)
15 | {
16 | query = query.Where(specification.Criteria);
17 | }
18 |
19 | query = specification.Includes.Aggregate(query,
20 | (current, include) => current.Include(include));
21 |
22 | query = specification.IncludeStrings.Aggregate(query,
23 | (current, include) => current.Include(include));
24 |
25 | if (specification.OrderBy != null)
26 | {
27 | query = query.OrderBy(specification.OrderBy);
28 | }
29 | else if (specification.OrderByDescending != null)
30 | {
31 | query = query.OrderByDescending(specification.OrderByDescending);
32 | }
33 |
34 | if (specification.IsPagingEnabled)
35 | {
36 | query = query.Skip(specification.Skip)
37 | .Take(specification.Take);
38 | }
39 |
40 | return query;
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Website/src/GalaxyHotel.Website/Controllers/HomeController.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using AutoMapper.Mappers;
3 | using GalaxyHotel.Core.Models;
4 | using GalaxyHotel.Core.Services;
5 | using GalaxyHotel.Infrastructure.Data;
6 | using GalaxyHotel.Website.Models;
7 | using Microsoft.AspNetCore.Mvc;
8 | using Microsoft.Extensions.Logging;
9 | using System.Diagnostics;
10 | using System.Threading.Tasks;
11 |
12 | namespace GalaxyHotel.Website.Controllers
13 | {
14 | public class HomeController : Controller
15 | {
16 | private readonly ILogger _logger;
17 | private readonly GalaxyHotelContext _context;
18 | private readonly IMapper _mapper;
19 |
20 | public HomeController(ILogger logger, GalaxyHotelContext context, IMapper mapper)
21 | {
22 | _logger = logger;
23 | _context = context;
24 | _mapper = mapper;
25 | }
26 |
27 | public async Task Index()
28 | {
29 | var roomService = new RoomService(new Repository(_context), _mapper);
30 | var rooms = await roomService.GetAll();
31 |
32 | return View(new RoomViewModel(rooms));
33 | }
34 |
35 | public IActionResult Booking()
36 | {
37 | return View();
38 | }
39 |
40 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
41 | public IActionResult Error()
42 | {
43 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Libraries/src/GalaxyHotel.Core/Entities/ReservationAggregate/Reservation.cs:
--------------------------------------------------------------------------------
1 | using Ardalis.GuardClauses;
2 | using GalaxyHotel.Core.Interfaces;
3 | using System;
4 |
5 | namespace GalaxyHotel.Core.Entities.ReservationAggregate
6 | {
7 | public class Reservation : BaseEntity, IAggregateRoot
8 | {
9 | public DateTimeOffset CheckIn { get; private set; }
10 | public DateTimeOffset CheckOut { get; private set; }
11 | public int GuestId { get; private set; }
12 | public int RoomId { get; private set; }
13 | public decimal PricePerNight { get; private set; }
14 |
15 | public Reservation() { }
16 |
17 | public Reservation(DateTimeOffset checkIn, DateTimeOffset checkOut, int guestId, int roomId, decimal pricePerNight)
18 | {
19 | Guard.Against.NegativeOrZero(guestId, nameof(guestId));
20 | Guard.Against.NegativeOrZero(roomId, nameof(roomId));
21 | Guard.Against.NegativeOrZero(pricePerNight, nameof(pricePerNight));
22 |
23 | CheckIn = checkIn;
24 | CheckOut = checkOut;
25 | GuestId = guestId;
26 | RoomId = roomId;
27 | PricePerNight = pricePerNight;
28 | }
29 |
30 | public void UpdatePricePerNight(decimal pricePerNight)
31 | {
32 | PricePerNight = pricePerNight;
33 | }
34 |
35 | public void UpdateCheckOutDate(DateTimeOffset newCheckOutDate)
36 | {
37 | CheckOut = newCheckOutDate;
38 | }
39 |
40 | public decimal TotalPrice()
41 | {
42 | return PricePerNight * (decimal)(CheckOut.Subtract(CheckIn).TotalDays);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Libraries/src/GalaxyHotel.Infrastructure/Data/ModelBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using GalaxyHotel.Core.Models;
2 | using Microsoft.EntityFrameworkCore;
3 |
4 | namespace GalaxyHotel.Infrastructure.Data
5 | {
6 | public static class ModelBuilderExtensions
7 | {
8 | public static void CreateSchema(this ModelBuilder modelBuilder)
9 | {
10 | modelBuilder.Entity()
11 | .HasOne(r => r.Guest)
12 | .WithMany(r => r.Reservations)
13 | .OnDelete(DeleteBehavior.Cascade);
14 |
15 | modelBuilder.Entity()
16 | .HasOne(r => r.Room)
17 | .WithMany(r => r.Reservations)
18 | .OnDelete(DeleteBehavior.Cascade);
19 |
20 | modelBuilder.Entity()
21 | .HasMany(g => g.Reservations)
22 | .WithOne(g => g.Guest)
23 | .OnDelete(DeleteBehavior.Cascade);
24 |
25 | modelBuilder.Entity()
26 | .HasOne(g => g.Address)
27 | .WithOne(g => g.Guest)
28 | .OnDelete(DeleteBehavior.Cascade);
29 |
30 | modelBuilder.Entity()
31 | .HasOne(g => g.PaymentMethod)
32 | .WithOne(g => g.Guest)
33 | .OnDelete(DeleteBehavior.Cascade);
34 |
35 | modelBuilder.Entity()
36 | .HasOne(g => g.Guest)
37 | .WithOne(g => g.Address)
38 | .OnDelete(DeleteBehavior.Cascade);
39 |
40 | modelBuilder.Entity()
41 | .HasOne(g => g.Guest)
42 | .WithOne(g => g.PaymentMethod)
43 | .OnDelete(DeleteBehavior.Cascade);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Website/src/GalaxyHotel.Website/wwwroot/lib/jquery/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright JS Foundation and other contributors, https://js.foundation/
2 |
3 | This software consists of voluntary contributions made by many
4 | individuals. For exact contribution history, see the revision history
5 | available at https://github.com/jquery/jquery
6 |
7 | The following license applies to all parts of this software except as
8 | documented below:
9 |
10 | ====
11 |
12 | Permission is hereby granted, free of charge, to any person obtaining
13 | a copy of this software and associated documentation files (the
14 | "Software"), to deal in the Software without restriction, including
15 | without limitation the rights to use, copy, modify, merge, publish,
16 | distribute, sublicense, and/or sell copies of the Software, and to
17 | permit persons to whom the Software is furnished to do so, subject to
18 | the following conditions:
19 |
20 | The above copyright notice and this permission notice shall be
21 | included in all copies or substantial portions of the Software.
22 |
23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 |
31 | ====
32 |
33 | All files located in the node_modules and external directories are
34 | externally maintained libraries used by this software which have their
35 | own licenses; we recommend you read them, as their terms may differ from
36 | the terms above.
37 |
--------------------------------------------------------------------------------
/Helpdesk/src/GalaxyHotel.HelpDesk/wwwroot/lib/jquery/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright JS Foundation and other contributors, https://js.foundation/
2 |
3 | This software consists of voluntary contributions made by many
4 | individuals. For exact contribution history, see the revision history
5 | available at https://github.com/jquery/jquery
6 |
7 | The following license applies to all parts of this software except as
8 | documented below:
9 |
10 | ====
11 |
12 | Permission is hereby granted, free of charge, to any person obtaining
13 | a copy of this software and associated documentation files (the
14 | "Software"), to deal in the Software without restriction, including
15 | without limitation the rights to use, copy, modify, merge, publish,
16 | distribute, sublicense, and/or sell copies of the Software, and to
17 | permit persons to whom the Software is furnished to do so, subject to
18 | the following conditions:
19 |
20 | The above copyright notice and this permission notice shall be
21 | included in all copies or substantial portions of the Software.
22 |
23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 |
31 | ====
32 |
33 | All files located in the node_modules and external directories are
34 | externally maintained libraries used by this software which have their
35 | own licenses; we recommend you read them, as their terms may differ from
36 | the terms above.
37 |
--------------------------------------------------------------------------------
/Helpdesk/src/GalaxyHotel.HelpDesk/wwwroot/css/site.css:
--------------------------------------------------------------------------------
1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
2 | for details on configuring this project to bundle and minify static web assets. */
3 |
4 | a.navbar-brand {
5 | white-space: normal;
6 | text-align: center;
7 | word-break: break-all;
8 | }
9 |
10 | /* Provide sufficient contrast against white background */
11 | a {
12 | color: #0366d6;
13 | }
14 |
15 | .btn-primary {
16 | color: #fff;
17 | background-color: #1b6ec2;
18 | border-color: #1861ac;
19 | }
20 |
21 | .nav-pills .nav-link.active, .nav-pills .show > .nav-link {
22 | color: #fff;
23 | background-color: #1b6ec2;
24 | border-color: #1861ac;
25 | }
26 |
27 | /* Sticky footer styles
28 | -------------------------------------------------- */
29 | html {
30 | font-size: 14px;
31 | }
32 | @media (min-width: 768px) {
33 | html {
34 | font-size: 16px;
35 | }
36 | }
37 |
38 | .border-top {
39 | border-top: 1px solid #e5e5e5;
40 | }
41 | .border-bottom {
42 | border-bottom: 1px solid #e5e5e5;
43 | }
44 |
45 | .box-shadow {
46 | box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
47 | }
48 |
49 | button.accept-policy {
50 | font-size: 1rem;
51 | line-height: inherit;
52 | }
53 |
54 | /* Sticky footer styles
55 | -------------------------------------------------- */
56 | html {
57 | position: relative;
58 | min-height: 100%;
59 | }
60 |
61 | body {
62 | /* Margin bottom by footer height */
63 | margin-bottom: 60px;
64 | }
65 | .footer {
66 | position: absolute;
67 | bottom: 0;
68 | width: 100%;
69 | white-space: nowrap;
70 | line-height: 60px; /* Vertically center the text there */
71 | }
72 |
73 | table {
74 | width: 80%;
75 | font-size: 20pt;
76 | text-align: center;
77 | margin-top: 200px;
78 | margin-left: auto;
79 | margin-right: auto;
80 | }
81 |
82 | tr {
83 | width: 200px;
84 | }
85 |
86 | td {
87 | width: 200px;
88 | }
--------------------------------------------------------------------------------
/Helpdesk/src/GalaxyHotel.HelpDesk/Controllers/HomeController.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using GalaxyHotel.Core.Models;
3 | using GalaxyHotel.Core.Services;
4 | using GalaxyHotel.HelpDesk.Models;
5 | using GalaxyHotel.Infrastructure.Data;
6 | using Microsoft.AspNetCore.Mvc;
7 | using Microsoft.Extensions.Logging;
8 | using System.Diagnostics;
9 | using System.Threading.Tasks;
10 |
11 | namespace GalaxyHotel.HelpDesk.Controllers
12 | {
13 | public class HomeController : Controller
14 | {
15 | private readonly ILogger _logger;
16 | private readonly GalaxyHotelContext _context;
17 | private readonly IMapper _mapper;
18 |
19 | public HomeController(ILogger logger, GalaxyHotelContext context, IMapper mapper)
20 | {
21 | _logger = logger;
22 | _context = context;
23 | _mapper = mapper;
24 | }
25 |
26 | public async Task Index()
27 | {
28 | var roomService = new RoomService(new Repository(_context), _mapper);
29 | var rooms = await roomService.GetAll();
30 |
31 | return View(new RoomViewModel(rooms));
32 | }
33 |
34 | public async Task Reservations()
35 | {
36 | var reservationService = new ReservationService(new ReservationRepository(_context), new Repository(_context), _mapper);
37 | var reservations = await reservationService.GetAll();
38 |
39 | return View(new ReservationViewModel(reservations));
40 | }
41 |
42 | public IActionResult Cancel()
43 | {
44 | return View();
45 | }
46 |
47 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
48 | public IActionResult Error()
49 | {
50 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Website/src/GalaxyHotel.Website/wwwroot/css/site.css:
--------------------------------------------------------------------------------
1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
2 | for details on configuring this project to bundle and minify static web assets. */
3 |
4 | a.navbar-brand {
5 | white-space: normal;
6 | text-align: center;
7 | word-break: break-all;
8 | }
9 |
10 | /* Provide sufficient contrast against white background */
11 | a {
12 | color: #0366d6;
13 | }
14 |
15 | .btn-primary {
16 | color: #fff;
17 | background-color: #1b6ec2;
18 | border-color: #1861ac;
19 | }
20 |
21 | .nav-pills .nav-link.active, .nav-pills .show > .nav-link {
22 | color: #fff;
23 | background-color: #1b6ec2;
24 | border-color: #1861ac;
25 | }
26 |
27 | /* Sticky footer styles
28 | -------------------------------------------------- */
29 | html {
30 | font-size: 14px;
31 | }
32 |
33 | @media (min-width: 768px) {
34 | html {
35 | font-size: 16px;
36 | }
37 | }
38 |
39 | .border-top {
40 | border-top: 1px solid #e5e5e5;
41 | }
42 |
43 | .border-bottom {
44 | border-bottom: 1px solid #e5e5e5;
45 | }
46 |
47 | .box-shadow {
48 | box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
49 | }
50 |
51 | button.accept-policy {
52 | font-size: 1rem;
53 | line-height: inherit;
54 | }
55 |
56 | /* Sticky footer styles
57 | -------------------------------------------------- */
58 | html {
59 | position: relative;
60 | min-height: 100%;
61 | }
62 |
63 | body {
64 | /* Margin bottom by footer height */
65 | margin-bottom: 60px;
66 | }
67 |
68 | .footer {
69 | position: absolute;
70 | bottom: 0;
71 | width: 100%;
72 | white-space: nowrap;
73 | line-height: 60px; /* Vertically center the text there */
74 | }
75 |
76 | table {
77 | width: 80%;
78 | font-size: 20pt;
79 | text-align: center;
80 | margin-top: 200px;
81 | margin-left: auto;
82 | margin-right: auto;
83 | }
84 |
85 | tr {
86 | width: 200px;
87 | }
88 |
89 | td {
90 | width: 200px;
91 | }
92 |
--------------------------------------------------------------------------------
/Website/src/GalaxyHotel.Website/Views/Shared/_Layout.cshtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | @ViewData["Title"] - GalaxyHotel.Website
7 |
8 |
9 |
10 |
11 |
30 |
31 |
32 | @RenderBody()
33 |
34 |
35 |
36 |
41 |
42 |
43 |
44 | @RenderSection("Scripts", required: false)
45 |
46 |
47 |
--------------------------------------------------------------------------------
/Libraries/src/GalaxyHotel.Core/Specification/BaseSpecifition.cs:
--------------------------------------------------------------------------------
1 | using GalaxyHotel.Core.Helpers.Query;
2 | using GalaxyHotel.Core.Interfaces;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq.Expressions;
6 |
7 | namespace GalaxyHotel.Core.Specification
8 | {
9 | public abstract class BaseSpecification : ISpecification
10 | {
11 | protected BaseSpecification() { }
12 |
13 | protected BaseSpecification(Expression> criteria)
14 | {
15 | Criteria = criteria;
16 | }
17 |
18 | public Expression> Criteria { get; }
19 | public List>> Includes { get; } = new List>>();
20 | public List IncludeStrings { get; } = new List();
21 | public Expression> OrderBy { get; private set; }
22 | public Expression> OrderByDescending { get; private set; }
23 |
24 | public int Take { get; private set; }
25 | public int Skip { get; private set; }
26 | public bool IsPagingEnabled { get; private set; } = false;
27 |
28 | protected virtual void AddInclude(Expression> includeExpression)
29 | {
30 | Includes.Add(includeExpression);
31 | }
32 |
33 | protected virtual void AddIncludes(Func, IIncludeQuery> includeGenerator)
34 | {
35 | var includeQuery = includeGenerator(new IncludeAggregator());
36 | IncludeStrings.AddRange(includeQuery.Paths);
37 | }
38 |
39 | protected virtual void AddInclude(string includeString)
40 | {
41 | IncludeStrings.Add(includeString);
42 | }
43 |
44 | protected virtual void ApplyPaging(int skip, int take)
45 | {
46 | Skip = skip;
47 | Take = take;
48 | IsPagingEnabled = true;
49 | }
50 |
51 | protected virtual void ApplyOrderBy(Expression> orderByExpression)
52 | {
53 | OrderBy = orderByExpression;
54 | }
55 |
56 | protected virtual void ApplyOrderByDescending(Expression> orderByDescendingExpression)
57 | {
58 | OrderByDescending = orderByDescendingExpression;
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Helpdesk/src/GalaxyHotel.HelpDesk/Views/Shared/_Layout.cshtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | @ViewData["Title"] - GalaxyHotel.HelpDesk
7 |
8 |
9 |
10 |
11 |
32 |
33 |
34 | @RenderBody()
35 |
36 |
37 |
38 |
43 |
44 |
45 |
46 | @RenderSection("Scripts", required: false)
47 |
48 |
49 |
--------------------------------------------------------------------------------
/Libraries/src/GalaxyHotel.Infrastructure/Data/Repository.cs:
--------------------------------------------------------------------------------
1 | using GalaxyHotel.Core.Interfaces;
2 | using GalaxyHotel.Core.Models;
3 | using Microsoft.EntityFrameworkCore;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 |
8 | namespace GalaxyHotel.Infrastructure.Data
9 | {
10 | public class Repository : IAsyncRepository where T : BaseEntity//, IAggregateRoot
11 | {
12 | protected readonly GalaxyHotelContext dbContext;
13 |
14 | public Repository(GalaxyHotelContext dbContext)
15 | {
16 | this.dbContext = dbContext;
17 | }
18 |
19 | public virtual async Task GetByIdAsync(int id)
20 | {
21 | return await dbContext.Set().FindAsync(id);
22 | }
23 |
24 | public virtual async Task> ListAllAsync()
25 | {
26 | return await dbContext.Set().ToListAsync();
27 | }
28 |
29 | public async Task> ListAsync(ISpecification spec)
30 | {
31 | return await ApplySpecification(spec).ToListAsync();
32 | }
33 |
34 | public async Task CountAsync(ISpecification spec)
35 | {
36 | return await ApplySpecification(spec).CountAsync();
37 | }
38 |
39 | public async Task AddAsync(T entity)
40 | {
41 | await dbContext.Set().AddAsync(entity);
42 | await dbContext.SaveChangesAsync();
43 |
44 | return entity;
45 | }
46 |
47 | public async Task UpdateAsync(T entity)
48 | {
49 | dbContext.Entry(entity).State = EntityState.Modified;
50 | await dbContext.SaveChangesAsync();
51 | }
52 |
53 | public async Task DeleteAsync(T entity)
54 | {
55 | dbContext.Set().Remove(entity);
56 | await dbContext.SaveChangesAsync();
57 | }
58 |
59 | public async Task FirstAsync(ISpecification spec)
60 | {
61 | return await ApplySpecification(spec).FirstAsync();
62 | }
63 |
64 | public async Task FirstOrDefaultAsync(ISpecification spec)
65 | {
66 | return await ApplySpecification(spec).FirstOrDefaultAsync();
67 | }
68 |
69 | private IQueryable ApplySpecification(ISpecification spec)
70 | {
71 | return SpecificationEvaluator.GetQuery(dbContext.Set().AsQueryable(), spec);
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/Website/GalaxyHotel.Website.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30204.135
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EC94B521-83F2-49FF-AD14-4B978CD271B3}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{D8F2C080-3D9D-4D9F-BF1D-B6F6FCCC6877}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GalaxyHotel.Website", "src\GalaxyHotel.Website\GalaxyHotel.Website.csproj", "{DA0F1A87-71D4-4047-A994-15E934ADB858}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GalaxyHotel.UITests", "tests\GalaxyHotel.UITests\GalaxyHotel.UITests.csproj", "{B623DDD1-C43E-46AD-8094-D26851D3E35D}"
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "db", "db", "{23FFBAD7-12B9-442D-A59A-5FD851B3EDF4}"
15 | ProjectSection(SolutionItems) = preProject
16 | db\SeedGalaxyHotelDbScript.sql = db\SeedGalaxyHotelDbScript.sql
17 | EndProjectSection
18 | EndProject
19 | Global
20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
21 | Debug|Any CPU = Debug|Any CPU
22 | Release|Any CPU = Release|Any CPU
23 | EndGlobalSection
24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
25 | {DA0F1A87-71D4-4047-A994-15E934ADB858}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26 | {DA0F1A87-71D4-4047-A994-15E934ADB858}.Debug|Any CPU.Build.0 = Debug|Any CPU
27 | {DA0F1A87-71D4-4047-A994-15E934ADB858}.Release|Any CPU.ActiveCfg = Release|Any CPU
28 | {DA0F1A87-71D4-4047-A994-15E934ADB858}.Release|Any CPU.Build.0 = Release|Any CPU
29 | {B623DDD1-C43E-46AD-8094-D26851D3E35D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30 | {B623DDD1-C43E-46AD-8094-D26851D3E35D}.Debug|Any CPU.Build.0 = Debug|Any CPU
31 | {B623DDD1-C43E-46AD-8094-D26851D3E35D}.Release|Any CPU.ActiveCfg = Release|Any CPU
32 | {B623DDD1-C43E-46AD-8094-D26851D3E35D}.Release|Any CPU.Build.0 = Release|Any CPU
33 | EndGlobalSection
34 | GlobalSection(SolutionProperties) = preSolution
35 | HideSolutionNode = FALSE
36 | EndGlobalSection
37 | GlobalSection(NestedProjects) = preSolution
38 | {DA0F1A87-71D4-4047-A994-15E934ADB858} = {EC94B521-83F2-49FF-AD14-4B978CD271B3}
39 | {B623DDD1-C43E-46AD-8094-D26851D3E35D} = {D8F2C080-3D9D-4D9F-BF1D-B6F6FCCC6877}
40 | EndGlobalSection
41 | GlobalSection(ExtensibilityGlobals) = postSolution
42 | SolutionGuid = {3F2CA0D0-68C8-42E2-BE75-EB4DFD8A3E81}
43 | EndGlobalSection
44 | EndGlobal
45 |
--------------------------------------------------------------------------------
/Libraries/src/GalaxyHotel.Core/Services/ReservationService.cs:
--------------------------------------------------------------------------------
1 | using Ardalis.GuardClauses;
2 | using AutoMapper;
3 | using GalaxyHotel.Core.Extensions;
4 | using GalaxyHotel.Core.Interfaces;
5 | using GalaxyHotel.Core.Models;
6 | using GalaxyHotel.Core.Specification;
7 | using GalaxyHotel.Dto;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Threading.Tasks;
11 |
12 | namespace GalaxyHotel.Core.Services
13 | {
14 | public class ReservationService : IReservationService
15 | {
16 | private readonly IAsyncRepository reservationRepository;
17 | private readonly IAsyncRepository roomRepository;
18 | private readonly IMapper mapper;
19 |
20 | public ReservationService(IAsyncRepository reservationRepository, IAsyncRepository roomRepository,
21 | IMapper mapper)
22 | {
23 | this.reservationRepository = reservationRepository;
24 | this.roomRepository = roomRepository;
25 | this.mapper = mapper;
26 | }
27 |
28 | public async Task CancelReservationAsync(int reservationId)
29 | {
30 | var reservation = await reservationRepository.GetByIdAsync(reservationId);
31 | Guard.Against.NotExistingReservation(reservation);
32 |
33 | await reservationRepository.DeleteAsync(reservation);
34 | }
35 |
36 | public async Task CreateReservationAsync(DateTimeOffset checkIn, DateTimeOffset checkOut, int guestId, int roomId)
37 | {
38 | var room = await roomRepository.GetByIdAsync(roomId);
39 | Guard.Against.Null(room, nameof(room));
40 |
41 | var reservation = new Reservation
42 | {
43 | CheckIn = checkIn,
44 | CheckOut = checkOut,
45 | GuestId = guestId,
46 | RoomId = roomId,
47 | TotalPrice = room.PricePerNight
48 | };
49 |
50 | await reservationRepository.AddAsync(reservation);
51 | }
52 |
53 | public async Task> GetAll()
54 | {
55 | var specification = new ReservationWithDetailsSpecification();
56 | var reservations = await reservationRepository.ListAsync(specification);
57 | return mapper.Map>(reservations);
58 | }
59 |
60 | public async Task GetByIdAsync(int reservationId)
61 | {
62 | var reservation = await reservationRepository.GetByIdAsync(reservationId);
63 | return mapper.Map(reservation);
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/Helpdesk/src/GalaxyHotel.HelpDesk/Startup.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using GalaxyHotel.Core.Mapping;
3 | using GalaxyHotel.Infrastructure.Data;
4 | using Microsoft.AspNetCore.Builder;
5 | using Microsoft.AspNetCore.Hosting;
6 | using Microsoft.EntityFrameworkCore;
7 | using Microsoft.Extensions.Configuration;
8 | using Microsoft.Extensions.DependencyInjection;
9 | using Microsoft.Extensions.Hosting;
10 |
11 | namespace GalaxyHotel.HelpDesk
12 | {
13 | public class Startup
14 | {
15 | public Startup(IConfiguration configuration)
16 | {
17 | Configuration = configuration;
18 | }
19 |
20 | public IConfiguration Configuration { get; }
21 |
22 | // This method gets called by the runtime. Use this method to add services to the container.
23 | public void ConfigureServices(IServiceCollection services)
24 | {
25 | services.AddDbContext(options =>
26 | options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
27 | b => b.MigrationsAssembly("GalaxyHotel.HelpDesk")));
28 | services.AddAutoMapper(typeof(Startup), typeof(GalaxyHotelProfile));
29 | services.AddControllersWithViews();
30 | }
31 |
32 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
33 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
34 | {
35 | using (var serviceScope = app.ApplicationServices.GetService().CreateScope())
36 | {
37 | var galaxyHotelContext = serviceScope.ServiceProvider.GetRequiredService();
38 | galaxyHotelContext.Database.EnsureCreated();
39 | if (env.IsDevelopment()) galaxyHotelContext.Database.Migrate();
40 | }
41 |
42 | if (env.IsDevelopment())
43 | {
44 | app.UseDeveloperExceptionPage();
45 | }
46 | else
47 | {
48 | app.UseExceptionHandler("/Home/Error");
49 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
50 | app.UseHsts();
51 | }
52 | app.UseHttpsRedirection();
53 | app.UseStaticFiles();
54 |
55 | app.UseRouting();
56 |
57 | app.UseAuthorization();
58 |
59 | app.UseEndpoints(endpoints =>
60 | {
61 | endpoints.MapControllerRoute(
62 | name: "default",
63 | pattern: "{controller=Home}/{action=Index}/{id?}");
64 | });
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Libraries/src/GalaxyHotel.Core/Helpers/Query/IncludeQueryExtensions.cs:
--------------------------------------------------------------------------------
1 | using GalaxyHotel.Core.Interfaces;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq.Expressions;
5 |
6 | namespace GalaxyHotel.Core.Helpers.Query
7 | {
8 | public static class IncludeQueryExtensions
9 | {
10 | public static IIncludeQuery Include(
11 | this IIncludeQuery query,
12 | Expression> selector)
13 | {
14 | query.Visitor.Visit(selector);
15 |
16 | var includeQuery = new IncludeQuery(query.PathMap);
17 | query.PathMap[includeQuery] = query.Visitor.Path;
18 |
19 | return includeQuery;
20 | }
21 |
22 | public static IIncludeQuery ThenInclude(
23 | this IIncludeQuery query,
24 | Expression> selector)
25 | {
26 | query.Visitor.Visit(selector);
27 |
28 | // If the visitor did not generated a path, return a new IncludeQuery with an unmodified PathMap.
29 | if (string.IsNullOrEmpty(query.Visitor.Path))
30 | {
31 | return new IncludeQuery(query.PathMap);
32 | }
33 |
34 | var pathMap = query.PathMap;
35 | var existingPath = pathMap[query];
36 | pathMap.Remove(query);
37 |
38 | var includeQuery = new IncludeQuery(query.PathMap);
39 | pathMap[includeQuery] = $"{existingPath}.{query.Visitor.Path}";
40 |
41 | return includeQuery;
42 | }
43 |
44 | public static IIncludeQuery ThenInclude(
45 | this IIncludeQuery> query,
46 | Expression> selector)
47 | {
48 | query.Visitor.Visit(selector);
49 |
50 | // If the visitor did not generated a path, return a new IncludeQuery with an unmodified PathMap.
51 | if (string.IsNullOrEmpty(query.Visitor.Path))
52 | {
53 | return new IncludeQuery(query.PathMap);
54 | }
55 |
56 | var pathMap = query.PathMap;
57 | var existingPath = pathMap[query];
58 | pathMap.Remove(query);
59 |
60 | var includeQuery = new IncludeQuery(query.PathMap);
61 | pathMap[includeQuery] = $"{existingPath}.{query.Visitor.Path}";
62 |
63 | return includeQuery;
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Website/src/GalaxyHotel.Website/Startup.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using GalaxyHotel.Core.Mapping;
3 | using GalaxyHotel.Infrastructure.Data;
4 | using GalaxyHotel.Website.Data;
5 | using Microsoft.AspNetCore.Builder;
6 | using Microsoft.AspNetCore.Hosting;
7 | using Microsoft.AspNetCore.Identity;
8 | using Microsoft.EntityFrameworkCore;
9 | using Microsoft.Extensions.Configuration;
10 | using Microsoft.Extensions.DependencyInjection;
11 | using Microsoft.Extensions.Hosting;
12 |
13 | namespace GalaxyHotel.Website
14 | {
15 | public class Startup
16 | {
17 | public Startup(IConfiguration configuration)
18 | {
19 | Configuration = configuration;
20 | }
21 |
22 | public IConfiguration Configuration { get; }
23 |
24 | // This method gets called by the runtime. Use this method to add services to the container.
25 | public void ConfigureServices(IServiceCollection services)
26 | {
27 | services.AddDbContext(options =>
28 | options.UseSqlServer(
29 | Configuration.GetConnectionString("DefaultConnection")));
30 | services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true)
31 | .AddEntityFrameworkStores();
32 | services.AddDbContext(options =>
33 | options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
34 | b => b.MigrationsAssembly("GalaxyHotel.Website")));
35 | services.AddAutoMapper(typeof(Startup), typeof(GalaxyHotelProfile));
36 | services.AddControllersWithViews();
37 | services.AddRazorPages();
38 | }
39 |
40 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
41 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
42 | {
43 | using (var serviceScope = app.ApplicationServices.GetService().CreateScope())
44 | {
45 | var galaxyHotelContext = serviceScope.ServiceProvider.GetRequiredService();
46 | galaxyHotelContext.Database.EnsureCreated();
47 | if (env.IsDevelopment()) galaxyHotelContext.Database.Migrate();
48 |
49 | var identityContext = serviceScope.ServiceProvider.GetRequiredService();
50 | identityContext.Database.Migrate();
51 | }
52 |
53 | if (env.IsDevelopment())
54 | {
55 | app.UseDeveloperExceptionPage();
56 | app.UseDatabaseErrorPage();
57 | }
58 | else
59 | {
60 | app.UseExceptionHandler("/Home/Error");
61 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
62 | app.UseHsts();
63 | }
64 | app.UseHttpsRedirection();
65 | app.UseStaticFiles();
66 |
67 | app.UseRouting();
68 |
69 | app.UseAuthentication();
70 | app.UseAuthorization();
71 |
72 | app.UseEndpoints(endpoints =>
73 | {
74 | endpoints.MapControllerRoute(
75 | name: "default",
76 | pattern: "{controller=Home}/{action=Index}/{id?}");
77 | endpoints.MapRazorPages();
78 | });
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Helpdesk/src/GalaxyHotel.HelpDesk/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Reboot v4.3.1 (https://getbootstrap.com/)
3 | * Copyright 2011-2019 The Bootstrap Authors
4 | * Copyright 2011-2019 Twitter, Inc.
5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
7 | */*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */
--------------------------------------------------------------------------------
/Website/src/GalaxyHotel.Website/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Reboot v4.3.1 (https://getbootstrap.com/)
3 | * Copyright 2011-2019 The Bootstrap Authors
4 | * Copyright 2011-2019 Twitter, Inc.
5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
7 | */*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */
--------------------------------------------------------------------------------
/Website/db/SeedGalaxyHotelDbScript.sql:
--------------------------------------------------------------------------------
1 | DELETE FROM [dbo].[Reservations]
2 | GO
3 | DELETE FROM [dbo].[Guests]
4 | GO
5 | DELETE FROM [dbo].[Rooms]
6 | GO
7 | DELETE FROM [dbo].[PaymentMethods]
8 | GO
9 | DELETE FROM [dbo].[Addresses]
10 | GO
11 |
12 | SET IDENTITY_INSERT [dbo].[Addresses] ON
13 | GO
14 | INSERT [dbo].[Addresses] ([Id], [Street], [City], [State], [Country], [ZipCode]) VALUES (1, N'13th Space Road, Brooklyn', N'New York', N'New York', N'United States of America', N'13021')
15 | GO
16 | INSERT [dbo].[Addresses] ([Id], [Street], [City], [State], [Country], [ZipCode]) VALUES (2, N'12th Hollywood road, Hollywood', N'Los Angeles', N'New York', N'United States of America', N'33024')
17 | GO
18 | INSERT [dbo].[Addresses] ([Id], [Street], [City], [State], [Country], [ZipCode]) VALUES (3, N'Piazza Plebiscito 1, Naples', N'Naples', N'', N'Italy', N'80100')
19 | GO
20 | INSERT [dbo].[Addresses] ([Id], [Street], [City], [State], [Country], [ZipCode]) VALUES (4, N'14th Valley Road', N'London', N'New York', N'United States of America', N'WC2N 5DU')
21 | GO
22 | SET IDENTITY_INSERT [dbo].[Addresses] OFF
23 | GO
24 | SET IDENTITY_INSERT [dbo].[PaymentMethods] ON
25 | GO
26 | INSERT [dbo].[PaymentMethods] ([Id], [Alias], [CardNumber], [DueDate]) VALUES (1, N'Private Credit Card 1', N'123412349876598765', CAST(N'2021-08-28T10:25:09.4799091+02:00' AS DateTimeOffset))
27 | GO
28 | INSERT [dbo].[PaymentMethods] ([Id], [Alias], [CardNumber], [DueDate]) VALUES (2, N'Private Credit Card 2', N'123412349876598766', CAST(N'2021-08-28T10:25:09.4871490+02:00' AS DateTimeOffset))
29 | GO
30 | INSERT [dbo].[PaymentMethods] ([Id], [Alias], [CardNumber], [DueDate]) VALUES (3, N'Private Credit Card 3', N'123412349876598767', CAST(N'2021-08-28T10:25:09.4871614+02:00' AS DateTimeOffset))
31 | GO
32 | INSERT [dbo].[PaymentMethods] ([Id], [Alias], [CardNumber], [DueDate]) VALUES (4, N'Private Credit Card 4', N'123412349876598768', CAST(N'2021-08-28T10:25:09.4871635+02:00' AS DateTimeOffset))
33 | GO
34 | SET IDENTITY_INSERT [dbo].[PaymentMethods] OFF
35 | GO
36 | SET IDENTITY_INSERT [dbo].[Guests] ON
37 | GO
38 | INSERT [dbo].[Guests] ([Id], [FirstName], [LastName], [AddressId], [PaymentMethodId]) VALUES (1, N'John', N'Doe', 1, 1)
39 | GO
40 | INSERT [dbo].[Guests] ([Id], [FirstName], [LastName], [AddressId], [PaymentMethodId]) VALUES (2, N'Alex', N'Harvey', 2, 2)
41 | GO
42 | INSERT [dbo].[Guests] ([Id], [FirstName], [LastName], [AddressId], [PaymentMethodId]) VALUES (3, N'Antonio', N'Esposito', 3, 3)
43 | GO
44 | INSERT [dbo].[Guests] ([Id], [FirstName], [LastName], [AddressId], [PaymentMethodId]) VALUES (4, N'Mark', N'River', 4, 4)
45 | GO
46 | SET IDENTITY_INSERT [dbo].[Guests] OFF
47 | GO
48 | SET IDENTITY_INSERT [dbo].[Rooms] ON
49 | GO
50 | INSERT [dbo].[Rooms] ([Id], [Name], [Floor], [PricePerNight]) VALUES (1, N'Andromeda', 1, CAST(200.00 AS Decimal(18, 2)))
51 | GO
52 | INSERT [dbo].[Rooms] ([Id], [Name], [Floor], [PricePerNight]) VALUES (2, N'Whirpool', 1, CAST(150.00 AS Decimal(18, 2)))
53 | GO
54 | INSERT [dbo].[Rooms] ([Id], [Name], [Floor], [PricePerNight]) VALUES (3, N'Sombrero', 1, CAST(100.00 AS Decimal(18, 2)))
55 | GO
56 | SET IDENTITY_INSERT [dbo].[Rooms] OFF
57 | GO
58 | SET IDENTITY_INSERT [dbo].[Reservations] ON
59 | GO
60 | INSERT [dbo].[Reservations] ([Id], [CheckIn], [CheckOut], [TotalPrice], [GuestId], [RoomId]) VALUES (1, CAST(N'2020-08-28T10:25:09.4921003+02:00' AS DateTimeOffset), CAST(N'2020-08-29T10:25:09.4921050+02:00' AS DateTimeOffset), CAST(200.00 AS Decimal(18, 2)), 1, 1)
61 | GO
62 | INSERT [dbo].[Reservations] ([Id], [CheckIn], [CheckOut], [TotalPrice], [GuestId], [RoomId]) VALUES (2, CAST(N'2020-08-28T10:25:09.4921003+02:00' AS DateTimeOffset), CAST(N'2020-08-29T10:25:09.4921050+02:00' AS DateTimeOffset), CAST(150.00 AS Decimal(18, 2)), 2, 2)
63 | GO
64 | INSERT [dbo].[Reservations] ([Id], [CheckIn], [CheckOut], [TotalPrice], [GuestId], [RoomId]) VALUES (3, CAST(N'2020-08-28T10:25:09.4921003+02:00' AS DateTimeOffset), CAST(N'2020-08-29T10:25:09.4921050+02:00' AS DateTimeOffset), CAST(100.00 AS Decimal(18, 2)), 3, 3)
65 | GO
66 | SET IDENTITY_INSERT [dbo].[Reservations] OFF
67 | GO
68 |
--------------------------------------------------------------------------------
/Libraries/GalaxyHotel.Libraries.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30204.135
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GalaxyHotel.Core", "src\GalaxyHotel.Core\GalaxyHotel.Core.csproj", "{B359C0F4-02C6-4776-AA6F-8855B23EA978}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{048E1DA2-519E-4B47-864A-9F9A96C54C07}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GalaxyHotel.Dto", "src\GalaxyHotel.Dto\GalaxyHotel.Dto.csproj", "{85AE3219-9A17-4C8B-922D-076177312114}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GalaxyHotel.Infrastructure", "src\GalaxyHotel.Infrastructure\GalaxyHotel.Infrastructure.csproj", "{F1A30111-6929-4F0B-ADCD-5F01A2158A71}"
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{3A549608-93C5-45F3-BE4B-84105FCE9B9E}"
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GalaxyHotel.Core.Services.Tests", "tests\GalaxyHotel.Core.Services.Tests\GalaxyHotel.Core.Services.Tests.csproj", "{1C1CB7D0-49AD-401F-BCFD-962F07ED9F81}"
17 | EndProject
18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GalaxyHotel.Infrastructure.IntegrationTests", "tests\GalaxyHotel.Infrastructure.IntegrationTests\GalaxyHotel.Infrastructure.IntegrationTests.csproj", "{CA831A3B-17EA-4678-B41C-A9D973FE6CE5}"
19 | EndProject
20 | Global
21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
22 | Debug|Any CPU = Debug|Any CPU
23 | Release|Any CPU = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 | {B359C0F4-02C6-4776-AA6F-8855B23EA978}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {B359C0F4-02C6-4776-AA6F-8855B23EA978}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {B359C0F4-02C6-4776-AA6F-8855B23EA978}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {B359C0F4-02C6-4776-AA6F-8855B23EA978}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {85AE3219-9A17-4C8B-922D-076177312114}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {85AE3219-9A17-4C8B-922D-076177312114}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {85AE3219-9A17-4C8B-922D-076177312114}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {85AE3219-9A17-4C8B-922D-076177312114}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {F1A30111-6929-4F0B-ADCD-5F01A2158A71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {F1A30111-6929-4F0B-ADCD-5F01A2158A71}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {F1A30111-6929-4F0B-ADCD-5F01A2158A71}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {F1A30111-6929-4F0B-ADCD-5F01A2158A71}.Release|Any CPU.Build.0 = Release|Any CPU
38 | {1C1CB7D0-49AD-401F-BCFD-962F07ED9F81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 | {1C1CB7D0-49AD-401F-BCFD-962F07ED9F81}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 | {1C1CB7D0-49AD-401F-BCFD-962F07ED9F81}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {1C1CB7D0-49AD-401F-BCFD-962F07ED9F81}.Release|Any CPU.Build.0 = Release|Any CPU
42 | {CA831A3B-17EA-4678-B41C-A9D973FE6CE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
43 | {CA831A3B-17EA-4678-B41C-A9D973FE6CE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
44 | {CA831A3B-17EA-4678-B41C-A9D973FE6CE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
45 | {CA831A3B-17EA-4678-B41C-A9D973FE6CE5}.Release|Any CPU.Build.0 = Release|Any CPU
46 | EndGlobalSection
47 | GlobalSection(SolutionProperties) = preSolution
48 | HideSolutionNode = FALSE
49 | EndGlobalSection
50 | GlobalSection(NestedProjects) = preSolution
51 | {B359C0F4-02C6-4776-AA6F-8855B23EA978} = {048E1DA2-519E-4B47-864A-9F9A96C54C07}
52 | {85AE3219-9A17-4C8B-922D-076177312114} = {048E1DA2-519E-4B47-864A-9F9A96C54C07}
53 | {F1A30111-6929-4F0B-ADCD-5F01A2158A71} = {048E1DA2-519E-4B47-864A-9F9A96C54C07}
54 | {1C1CB7D0-49AD-401F-BCFD-962F07ED9F81} = {3A549608-93C5-45F3-BE4B-84105FCE9B9E}
55 | {CA831A3B-17EA-4678-B41C-A9D973FE6CE5} = {3A549608-93C5-45F3-BE4B-84105FCE9B9E}
56 | EndGlobalSection
57 | GlobalSection(ExtensibilityGlobals) = postSolution
58 | SolutionGuid = {63F36747-965C-4623-AEEF-E4F2C635819F}
59 | EndGlobalSection
60 | EndGlobal
61 |
--------------------------------------------------------------------------------
/Website/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | trigger:
2 | - master
3 |
4 | pool:
5 | vmImage: 'windows-latest'
6 |
7 | variables:
8 | buildConfiguration: 'Release'
9 |
10 | stages:
11 | - stage: build
12 | displayName: Build
13 | jobs:
14 | - job: Build
15 | steps:
16 | - template: build-website.yml
17 | parameters:
18 | publishArgs: '--output $(Build.ArtifactStagingDirectory)'
19 | - task: PublishPipelineArtifact@1
20 | displayName: Publish UI Tests artifacts
21 | inputs:
22 | targetPath: '$(Build.Repository.LocalPath)/tests/GalaxyHotel.UITests'
23 | artifact: 'UITests'
24 | publishLocation: 'pipeline'
25 | - task: PublishPipelineArtifact@1
26 | displayName: Publish db artifacts
27 | inputs:
28 | targetPath: '$(Build.Repository.LocalPath)/db'
29 | artifact: 'db'
30 | publishLocation: 'pipeline'
31 | - task: PublishPipelineArtifact@1
32 | displayName: Publish web artifacts
33 | inputs:
34 | targetPath: '$(Build.ArtifactStagingDirectory)'
35 | artifact: 'web'
36 | publishLocation: 'pipeline'
37 |
38 | - stage: deployDev
39 | displayName: Deploy to Dev
40 | jobs:
41 | - deployment: deployDev
42 | displayName: Deploy To Dev
43 | environment: 'GalaxyHotel-Dev'
44 | strategy:
45 | runOnce:
46 | deploy:
47 | steps:
48 | - task: AzureRmWebAppDeployment@4
49 | inputs:
50 | ConnectionType: 'AzureRM'
51 | azureSubscription: 'Microsoft Azure Sponsorship'
52 | appType: 'webApp'
53 | WebAppName: 'galaxyhotel-dev'
54 | packageForLinux: '$(Pipeline.Workspace)/web/**/*.zip'
55 |
56 | - stage: deployQA
57 | displayName: Deploy to QA
58 | jobs:
59 | - deployment: deployQAWebsite
60 | displayName: Deploy Website to QA
61 | environment: 'GalaxyHotel-QA'
62 | strategy:
63 | runOnce:
64 | deploy:
65 | steps:
66 | - task: SqlAzureDacpacDeployment@1
67 | displayName: Prepare the database
68 | inputs:
69 | azureSubscription: 'Microsoft Azure Sponsorship'
70 | AuthenticationType: 'server'
71 | ServerName: 'succintly.database.windows.net'
72 | DatabaseName: 'galaxyhotel-qa'
73 | SqlUsername: '$(sqlAdmin)'
74 | SqlPassword: '$(sqlPassword)'
75 | deployType: 'SqlTask'
76 | SqlFile: '$(Pipeline.Workspace)/db/SeedGalaxyHotelDbScript.sql'
77 | IpDetectionMethod: 'AutoDetect'
78 | - task: AzureRmWebAppDeployment@4
79 | displayName: Deploy website
80 | inputs:
81 | ConnectionType: 'AzureRM'
82 | azureSubscription: 'Microsoft Azure Sponsorship'
83 | appType: 'webApp'
84 | WebAppName: 'galaxyhotel-qa'
85 | packageForLinux: '$(Pipeline.Workspace)/web/**/*.zip'
86 | - deployment: runQATests
87 | displayName: Run QA Tests
88 | dependsOn: deployQAWebsite
89 | environment:
90 | name: 'GalaxyHotel-QA'
91 | resourceType: VirtualMachine
92 | tags: qa
93 | strategy:
94 | runOnce:
95 | deploy:
96 | steps:
97 | - download: none
98 | - task: DownloadPipelineArtifact@2
99 | displayName: Download UITests artifact
100 | inputs:
101 | buildType: 'current'
102 | artifactName: 'UITests'
103 | targetPath: '$(Pipeline.Workspace)/UITests'
104 | - task: PowerShell@2
105 | displayName: Override TestRunParameters
106 | inputs:
107 | targetType: 'inline'
108 | script: |
109 | [xml]$doc = Get-Content $(Pipeline.Workspace)/UITests/GalaxyHotelTests.runsettings
110 | $doc.RunSettings.TestRunParameters.ChildNodes.Item(0).value = '$(qaBaseUrl)'
111 | $doc.Save("$(Pipeline.Workspace)/UITests/GalaxyHotelTests.runsettings")
112 | - task: DotNetCoreCLI@2
113 | displayName: Execute UI Tests with Selenium
114 | inputs:
115 | command: 'test'
116 | projects: '$(Pipeline.Workspace)/UITests/**/*.csproj'
117 | arguments: '--settings $(Pipeline.Workspace)/UITests/GalaxyHotelTests.runsettings'
118 |
119 | - stage: deployProd
120 | displayName: Deploy to Production
121 | jobs:
122 | - deployment: deployProd
123 | displayName: Deploy To Production
124 | environment: 'GalaxyHotel-Prod'
125 | strategy:
126 | runOnce:
127 | deploy:
128 | steps:
129 | - task: AzureRmWebAppDeployment@4
130 | inputs:
131 | ConnectionType: 'AzureRM'
132 | azureSubscription: 'Microsoft Azure Sponsorship'
133 | appType: 'webApp'
134 | WebAppName: 'galaxyhotel'
135 | packageForLinux: '$(Pipeline.Workspace)/web/**/*.zip'
--------------------------------------------------------------------------------
/Libraries/tests/GalaxyHotel.Core.Services.Tests/ReservationServiceTests.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using GalaxyHotel.Core.Exceptions;
3 | using GalaxyHotel.Core.Interfaces;
4 | using GalaxyHotel.Core.Models;
5 | using Moq;
6 | using NUnit.Framework;
7 | using System;
8 | using System.Threading.Tasks;
9 |
10 | namespace GalaxyHotel.Core.Services.Tests
11 | {
12 | public class ReservationServiceTests
13 | {
14 | private Mock> reservationRepositoryMock;
15 | private Mock> roomRepositoryMock;
16 | private Mock mapperMock;
17 | private ReservationService reservationService;
18 | private readonly DateTimeOffset checkInDate = DateTimeOffset.Now;
19 | private readonly DateTimeOffset checkOutDate = DateTimeOffset.Now.AddDays(1);
20 | private const int reservationId = 1;
21 | private const int notExistingReservationId = 2;
22 | private const int guestId = 1;
23 | private const int roomId = 1;
24 | private const int notExistingRoomId = 2;
25 | private const string roomName = "Andromeda";
26 | private const int roomFloor = 1;
27 | private const int roomTypeId = 1;
28 | private const decimal pricePerNight = 50;
29 |
30 | [SetUp]
31 | public void Setup()
32 | {
33 | reservationRepositoryMock = new Mock>();
34 | roomRepositoryMock = new Mock>();
35 | mapperMock = new Mock();
36 |
37 | var room = new Room { Name = roomName, Floor = roomFloor, PricePerNight = pricePerNight };
38 | Room notExistingRoom = null;
39 | roomRepositoryMock.Setup(r => r.GetByIdAsync(roomId)).ReturnsAsync(room);
40 | roomRepositoryMock.Setup(r => r.GetByIdAsync(notExistingRoomId)).ReturnsAsync(notExistingRoom);
41 |
42 | Reservation notExistingReservation = null;
43 | Reservation reservation = new Reservation
44 | {
45 | Id = 1,
46 | CheckIn = checkInDate,
47 | CheckOut = checkOutDate,
48 | GuestId = guestId,
49 | RoomId = roomId,
50 | TotalPrice = pricePerNight
51 | };
52 | reservationRepositoryMock.Setup(r => r.AddAsync(It.IsAny())).Verifiable();
53 | reservationRepositoryMock.Setup(r => r.GetByIdAsync(reservationId)).ReturnsAsync(reservation).Verifiable();
54 | reservationRepositoryMock.Setup(r => r.ListAllAsync()).Verifiable();
55 | reservationRepositoryMock.Setup(r => r.GetByIdAsync(notExistingReservationId)).ReturnsAsync(notExistingReservation);
56 | reservationRepositoryMock.Setup(r => r.DeleteAsync(It.IsAny())).Verifiable();
57 |
58 | reservationService = new ReservationService(reservationRepositoryMock.Object, roomRepositoryMock.Object, mapperMock.Object);
59 | }
60 |
61 | [Test]
62 | public async Task CreateReservation_RoomAvailable_ReservationCreated()
63 | {
64 | await reservationService.CreateReservationAsync(checkInDate, checkOutDate, guestId, roomId);
65 |
66 | reservationRepositoryMock.Verify(m => m.AddAsync(It.IsAny()), Times.Once);
67 | }
68 |
69 | [Test]
70 | public void CreateReservation_RoomNotExisting_ExceptionThrown()
71 | {
72 | Assert.That(() => reservationService.CreateReservationAsync(checkInDate, checkOutDate, guestId, notExistingRoomId),
73 | Throws.ArgumentNullException);
74 | }
75 |
76 | [Test]
77 | public async Task GetReservationById_ReservationExisting_ReservationReturned()
78 | {
79 | await reservationService.GetByIdAsync(reservationId);
80 |
81 | reservationRepositoryMock.Verify(m => m.GetByIdAsync(reservationId), Times.Once);
82 | }
83 |
84 | [Test]
85 | public async Task GetReservations_ReservationsExisting_ReservationsReturned()
86 | {
87 | await reservationService.GetAll();
88 |
89 | reservationRepositoryMock.Verify(m => m.ListAsync(It.IsAny>()), Times.Once);
90 | }
91 |
92 | [Test]
93 | public async Task CancelReservation_ReservationExisting_ReservationCanceled()
94 | {
95 | await reservationService.CancelReservationAsync(reservationId);
96 |
97 | reservationRepositoryMock.Verify(m => m.GetByIdAsync(reservationId), Times.Once);
98 | reservationRepositoryMock.Verify(m => m.DeleteAsync(It.IsAny()), Times.Once);
99 | }
100 |
101 | [Test]
102 | public void CancelReservation_ReservationNotExisting_ExceptionThrown()
103 | {
104 | Assert.That(() => reservationService.CancelReservationAsync(notExistingReservationId),
105 | Throws.TypeOf());
106 | }
107 | }
108 | }
--------------------------------------------------------------------------------
/Helpdesk/src/GalaxyHotel.HelpDesk/Migrations/20200829211346_SeedGalaxyHotelDb.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | namespace GalaxyHotel.HelpDesk.Migrations
4 | {
5 | public partial class SeedGalaxyHotelDb : Migration
6 | {
7 | protected override void Up(MigrationBuilder migrationBuilder)
8 | {
9 | migrationBuilder.Sql(
10 | @"SET IDENTITY_INSERT [dbo].[Addresses] ON
11 | GO
12 | INSERT [dbo].[Addresses] ([Id], [Street], [City], [State], [Country], [ZipCode]) VALUES (1, N'13th Space Road, Brooklyn', N'New York', N'New York', N'United States of America', N'13021')
13 | GO
14 | INSERT [dbo].[Addresses] ([Id], [Street], [City], [State], [Country], [ZipCode]) VALUES (2, N'12th Hollywood road, Hollywood', N'Los Angeles', N'New York', N'United States of America', N'33024')
15 | GO
16 | INSERT [dbo].[Addresses] ([Id], [Street], [City], [State], [Country], [ZipCode]) VALUES (3, N'Piazza Plebiscito 1, Naples', N'Naples', N'', N'Italy', N'80100')
17 | GO
18 | INSERT [dbo].[Addresses] ([Id], [Street], [City], [State], [Country], [ZipCode]) VALUES (4, N'14th Valley Road', N'London', N'New York', N'United States of America', N'WC2N 5DU')
19 | GO
20 | SET IDENTITY_INSERT [dbo].[Addresses] OFF
21 | GO
22 | SET IDENTITY_INSERT [dbo].[PaymentMethods] ON
23 | GO
24 | INSERT [dbo].[PaymentMethods] ([Id], [Alias], [CardNumber], [DueDate]) VALUES (1, N'Private Credit Card 1', N'123412349876598765', CAST(N'2021-08-28T10:25:09.4799091+02:00' AS DateTimeOffset))
25 | GO
26 | INSERT [dbo].[PaymentMethods] ([Id], [Alias], [CardNumber], [DueDate]) VALUES (2, N'Private Credit Card 2', N'123412349876598766', CAST(N'2021-08-28T10:25:09.4871490+02:00' AS DateTimeOffset))
27 | GO
28 | INSERT [dbo].[PaymentMethods] ([Id], [Alias], [CardNumber], [DueDate]) VALUES (3, N'Private Credit Card 3', N'123412349876598767', CAST(N'2021-08-28T10:25:09.4871614+02:00' AS DateTimeOffset))
29 | GO
30 | INSERT [dbo].[PaymentMethods] ([Id], [Alias], [CardNumber], [DueDate]) VALUES (4, N'Private Credit Card 4', N'123412349876598768', CAST(N'2021-08-28T10:25:09.4871635+02:00' AS DateTimeOffset))
31 | GO
32 | SET IDENTITY_INSERT [dbo].[PaymentMethods] OFF
33 | GO
34 | SET IDENTITY_INSERT [dbo].[Guests] ON
35 | GO
36 | INSERT [dbo].[Guests] ([Id], [FirstName], [LastName], [AddressId], [PaymentMethodId]) VALUES (1, N'John', N'Doe', 1, 1)
37 | GO
38 | INSERT [dbo].[Guests] ([Id], [FirstName], [LastName], [AddressId], [PaymentMethodId]) VALUES (2, N'Alex', N'Harvey', 2, 2)
39 | GO
40 | INSERT [dbo].[Guests] ([Id], [FirstName], [LastName], [AddressId], [PaymentMethodId]) VALUES (3, N'Antonio', N'Esposito', 3, 3)
41 | GO
42 | INSERT [dbo].[Guests] ([Id], [FirstName], [LastName], [AddressId], [PaymentMethodId]) VALUES (4, N'Mark', N'River', 4, 4)
43 | GO
44 | SET IDENTITY_INSERT [dbo].[Guests] OFF
45 | GO
46 | SET IDENTITY_INSERT [dbo].[Rooms] ON
47 | GO
48 | INSERT [dbo].[Rooms] ([Id], [Name], [Floor], [PricePerNight]) VALUES (1, N'Andromeda', 1, CAST(200.00 AS Decimal(18, 2)))
49 | GO
50 | INSERT [dbo].[Rooms] ([Id], [Name], [Floor], [PricePerNight]) VALUES (2, N'Whirpool', 1, CAST(150.00 AS Decimal(18, 2)))
51 | GO
52 | INSERT [dbo].[Rooms] ([Id], [Name], [Floor], [PricePerNight]) VALUES (3, N'Sombrero', 1, CAST(100.00 AS Decimal(18, 2)))
53 | GO
54 | SET IDENTITY_INSERT [dbo].[Rooms] OFF
55 | GO
56 | SET IDENTITY_INSERT [dbo].[Reservations] ON
57 | GO
58 | INSERT [dbo].[Reservations] ([Id], [CheckIn], [CheckOut], [TotalPrice], [GuestId], [RoomId]) VALUES (1, CAST(N'2020-08-28T10:25:09.4921003+02:00' AS DateTimeOffset), CAST(N'2020-08-29T10:25:09.4921050+02:00' AS DateTimeOffset), CAST(200.00 AS Decimal(18, 2)), 1, 1)
59 | GO
60 | INSERT [dbo].[Reservations] ([Id], [CheckIn], [CheckOut], [TotalPrice], [GuestId], [RoomId]) VALUES (2, CAST(N'2020-08-28T10:25:09.4921003+02:00' AS DateTimeOffset), CAST(N'2020-08-29T10:25:09.4921050+02:00' AS DateTimeOffset), CAST(150.00 AS Decimal(18, 2)), 2, 2)
61 | GO
62 | INSERT [dbo].[Reservations] ([Id], [CheckIn], [CheckOut], [TotalPrice], [GuestId], [RoomId]) VALUES (3, CAST(N'2020-08-28T10:25:09.4921003+02:00' AS DateTimeOffset), CAST(N'2020-08-29T10:25:09.4921050+02:00' AS DateTimeOffset), CAST(100.00 AS Decimal(18, 2)), 3, 3)
63 | GO
64 | SET IDENTITY_INSERT [dbo].[Reservations] OFF
65 | GO
66 | ");
67 | }
68 |
69 | protected override void Down(MigrationBuilder migrationBuilder)
70 | {
71 |
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Website/src/GalaxyHotel.Website/Data/Migrations/00000000000000_SeedGalaxyHotelDb.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore.Migrations;
3 |
4 | namespace GalaxyHotel.Website.Migrations
5 | {
6 | public partial class SeedGalaxyHotelDb : Migration
7 | {
8 | protected override void Up(MigrationBuilder migrationBuilder)
9 | {
10 | migrationBuilder.Sql(
11 | @"SET IDENTITY_INSERT [dbo].[Addresses] ON
12 | GO
13 | INSERT [dbo].[Addresses] ([Id], [Street], [City], [State], [Country], [ZipCode]) VALUES (1, N'13th Space Road, Brooklyn', N'New York', N'New York', N'United States of America', N'13021')
14 | GO
15 | INSERT [dbo].[Addresses] ([Id], [Street], [City], [State], [Country], [ZipCode]) VALUES (2, N'12th Hollywood road, Hollywood', N'Los Angeles', N'New York', N'United States of America', N'33024')
16 | GO
17 | INSERT [dbo].[Addresses] ([Id], [Street], [City], [State], [Country], [ZipCode]) VALUES (3, N'Piazza Plebiscito 1, Naples', N'Naples', N'', N'Italy', N'80100')
18 | GO
19 | INSERT [dbo].[Addresses] ([Id], [Street], [City], [State], [Country], [ZipCode]) VALUES (4, N'14th Valley Road', N'London', N'New York', N'United States of America', N'WC2N 5DU')
20 | GO
21 | SET IDENTITY_INSERT [dbo].[Addresses] OFF
22 | GO
23 | SET IDENTITY_INSERT [dbo].[PaymentMethods] ON
24 | GO
25 | INSERT [dbo].[PaymentMethods] ([Id], [Alias], [CardNumber], [DueDate]) VALUES (1, N'Private Credit Card 1', N'123412349876598765', CAST(N'2021-08-28T10:25:09.4799091+02:00' AS DateTimeOffset))
26 | GO
27 | INSERT [dbo].[PaymentMethods] ([Id], [Alias], [CardNumber], [DueDate]) VALUES (2, N'Private Credit Card 2', N'123412349876598766', CAST(N'2021-08-28T10:25:09.4871490+02:00' AS DateTimeOffset))
28 | GO
29 | INSERT [dbo].[PaymentMethods] ([Id], [Alias], [CardNumber], [DueDate]) VALUES (3, N'Private Credit Card 3', N'123412349876598767', CAST(N'2021-08-28T10:25:09.4871614+02:00' AS DateTimeOffset))
30 | GO
31 | INSERT [dbo].[PaymentMethods] ([Id], [Alias], [CardNumber], [DueDate]) VALUES (4, N'Private Credit Card 4', N'123412349876598768', CAST(N'2021-08-28T10:25:09.4871635+02:00' AS DateTimeOffset))
32 | GO
33 | SET IDENTITY_INSERT [dbo].[PaymentMethods] OFF
34 | GO
35 | SET IDENTITY_INSERT [dbo].[Guests] ON
36 | GO
37 | INSERT [dbo].[Guests] ([Id], [FirstName], [LastName], [AddressId], [PaymentMethodId]) VALUES (1, N'John', N'Doe', 1, 1)
38 | GO
39 | INSERT [dbo].[Guests] ([Id], [FirstName], [LastName], [AddressId], [PaymentMethodId]) VALUES (2, N'Alex', N'Harvey', 2, 2)
40 | GO
41 | INSERT [dbo].[Guests] ([Id], [FirstName], [LastName], [AddressId], [PaymentMethodId]) VALUES (3, N'Antonio', N'Esposito', 3, 3)
42 | GO
43 | INSERT [dbo].[Guests] ([Id], [FirstName], [LastName], [AddressId], [PaymentMethodId]) VALUES (4, N'Mark', N'River', 4, 4)
44 | GO
45 | SET IDENTITY_INSERT [dbo].[Guests] OFF
46 | GO
47 | SET IDENTITY_INSERT [dbo].[Rooms] ON
48 | GO
49 | INSERT [dbo].[Rooms] ([Id], [Name], [Floor], [PricePerNight]) VALUES (1, N'Andromeda', 1, CAST(200.00 AS Decimal(18, 2)))
50 | GO
51 | INSERT [dbo].[Rooms] ([Id], [Name], [Floor], [PricePerNight]) VALUES (2, N'Whirpool', 1, CAST(150.00 AS Decimal(18, 2)))
52 | GO
53 | INSERT [dbo].[Rooms] ([Id], [Name], [Floor], [PricePerNight]) VALUES (3, N'Sombrero', 1, CAST(100.00 AS Decimal(18, 2)))
54 | GO
55 | SET IDENTITY_INSERT [dbo].[Rooms] OFF
56 | GO
57 | SET IDENTITY_INSERT [dbo].[Reservations] ON
58 | GO
59 | INSERT [dbo].[Reservations] ([Id], [CheckIn], [CheckOut], [TotalPrice], [GuestId], [RoomId]) VALUES (1, CAST(N'2020-08-28T10:25:09.4921003+02:00' AS DateTimeOffset), CAST(N'2020-08-29T10:25:09.4921050+02:00' AS DateTimeOffset), CAST(200.00 AS Decimal(18, 2)), 1, 1)
60 | GO
61 | INSERT [dbo].[Reservations] ([Id], [CheckIn], [CheckOut], [TotalPrice], [GuestId], [RoomId]) VALUES (2, CAST(N'2020-08-28T10:25:09.4921003+02:00' AS DateTimeOffset), CAST(N'2020-08-29T10:25:09.4921050+02:00' AS DateTimeOffset), CAST(150.00 AS Decimal(18, 2)), 2, 2)
62 | GO
63 | INSERT [dbo].[Reservations] ([Id], [CheckIn], [CheckOut], [TotalPrice], [GuestId], [RoomId]) VALUES (3, CAST(N'2020-08-28T10:25:09.4921003+02:00' AS DateTimeOffset), CAST(N'2020-08-29T10:25:09.4921050+02:00' AS DateTimeOffset), CAST(100.00 AS Decimal(18, 2)), 3, 3)
64 | GO
65 | SET IDENTITY_INSERT [dbo].[Reservations] OFF
66 | GO
67 | ");
68 | }
69 |
70 | protected override void Down(MigrationBuilder migrationBuilder)
71 | {
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Helpdesk/src/GalaxyHotel.HelpDesk/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js:
--------------------------------------------------------------------------------
1 | // Unobtrusive validation support library for jQuery and jQuery Validate
2 | // Copyright (c) .NET Foundation. All rights reserved.
3 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
4 | // @version v3.2.11
5 | !function(a){"function"==typeof define&&define.amd?define("jquery.validate.unobtrusive",["jquery-validation"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery-validation")):jQuery.validator.unobtrusive=a(jQuery)}(function(a){function e(a,e,n){a.rules[e]=n,a.message&&(a.messages[e]=a.message)}function n(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function t(a){return a.replace(/([!"#$%&'()*+,.\/:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function r(a){return a.substr(0,a.lastIndexOf(".")+1)}function i(a,e){return 0===a.indexOf("*.")&&(a=a.replace("*.",e)),a}function o(e,n){var r=a(this).find("[data-valmsg-for='"+t(n[0].name)+"']"),i=r.attr("data-valmsg-replace"),o=i?a.parseJSON(i)!==!1:null;r.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",r),o?(r.empty(),e.removeClass("input-validation-error").appendTo(r)):e.hide()}function d(e,n){var t=a(this).find("[data-valmsg-summary=true]"),r=t.find("ul");r&&r.length&&n.errorList.length&&(r.empty(),t.addClass("validation-summary-errors").removeClass("validation-summary-valid"),a.each(n.errorList,function(){a(" ").html(this.message).appendTo(r)}))}function s(e){var n=e.data("unobtrusiveContainer");if(n){var t=n.attr("data-valmsg-replace"),r=t?a.parseJSON(t):null;n.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),r&&n.empty()}}function l(e){var n=a(this),t="__jquery_unobtrusive_validation_form_reset";if(!n.data(t)){n.data(t,!0);try{n.data("validator").resetForm()}finally{n.removeData(t)}n.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),n.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function u(e){var n=a(e),t=n.data(v),r=a.proxy(l,e),i=f.unobtrusive.options||{},u=function(n,t){var r=i[n];r&&a.isFunction(r)&&r.apply(e,t)};return t||(t={options:{errorClass:i.errorClass||"input-validation-error",errorElement:i.errorElement||"span",errorPlacement:function(){o.apply(e,arguments),u("errorPlacement",arguments)},invalidHandler:function(){d.apply(e,arguments),u("invalidHandler",arguments)},messages:{},rules:{},success:function(){s.apply(e,arguments),u("success",arguments)}},attachValidation:function(){n.off("reset."+v,r).on("reset."+v,r).validate(this.options)},validate:function(){return n.validate(),n.valid()}},n.data(v,t)),t}var m,f=a.validator,v="unobtrusiveValidation";return f.unobtrusive={adapters:[],parseElement:function(e,n){var t,r,i,o=a(e),d=o.parents("form")[0];d&&(t=u(d),t.options.rules[e.name]=r={},t.options.messages[e.name]=i={},a.each(this.adapters,function(){var n="data-val-"+this.name,t=o.attr(n),s={};void 0!==t&&(n+="-",a.each(this.params,function(){s[this]=o.attr(n+this)}),this.adapt({element:e,form:d,message:t,params:s,rules:r,messages:i}))}),a.extend(r,{__dummy__:!0}),n||t.attachValidation())},parse:function(e){var n=a(e),t=n.parents().addBack().filter("form").add(n.find("form")).has("[data-val=true]");n.find("[data-val=true]").each(function(){f.unobtrusive.parseElement(this,!0)}),t.each(function(){var a=u(this);a&&a.attachValidation()})}},m=f.unobtrusive.adapters,m.add=function(a,e,n){return n||(n=e,e=[]),this.push({name:a,params:e,adapt:n}),this},m.addBool=function(a,n){return this.add(a,function(t){e(t,n||a,!0)})},m.addMinMax=function(a,n,t,r,i,o){return this.add(a,[i||"min",o||"max"],function(a){var i=a.params.min,o=a.params.max;i&&o?e(a,r,[i,o]):i?e(a,n,i):o&&e(a,t,o)})},m.addSingleVal=function(a,n,t){return this.add(a,[n||"val"],function(r){e(r,t||a,r.params[n])})},f.addMethod("__dummy__",function(a,e,n){return!0}),f.addMethod("regex",function(a,e,n){var t;return!!this.optional(e)||(t=new RegExp(n).exec(a),t&&0===t.index&&t[0].length===a.length)}),f.addMethod("nonalphamin",function(a,e,n){var t;return n&&(t=a.match(/\W/g),t=t&&t.length>=n),t}),f.methods.extension?(m.addSingleVal("accept","mimtype"),m.addSingleVal("extension","extension")):m.addSingleVal("extension","extension","accept"),m.addSingleVal("regex","pattern"),m.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),m.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),m.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),m.add("equalto",["other"],function(n){var o=r(n.element.name),d=n.params.other,s=i(d,o),l=a(n.form).find(":input").filter("[name='"+t(s)+"']")[0];e(n,"equalTo",l)}),m.add("required",function(a){"INPUT"===a.element.tagName.toUpperCase()&&"CHECKBOX"===a.element.type.toUpperCase()||e(a,"required",!0)}),m.add("remote",["url","type","additionalfields"],function(o){var d={url:o.params.url,type:o.params.type||"GET",data:{}},s=r(o.element.name);a.each(n(o.params.additionalfields||o.element.name),function(e,n){var r=i(n,s);d.data[r]=function(){var e=a(o.form).find(":input").filter("[name='"+t(r)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),e(o,"remote",d)}),m.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&e(a,"minlength",a.params.min),a.params.nonalphamin&&e(a,"nonalphamin",a.params.nonalphamin),a.params.regex&&e(a,"regex",a.params.regex)}),m.add("fileextensions",["extensions"],function(a){e(a,"extension",a.params.extensions)}),a(function(){f.unobtrusive.parse(document)}),f.unobtrusive});
--------------------------------------------------------------------------------
/Website/src/GalaxyHotel.Website/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js:
--------------------------------------------------------------------------------
1 | // Unobtrusive validation support library for jQuery and jQuery Validate
2 | // Copyright (c) .NET Foundation. All rights reserved.
3 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
4 | // @version v3.2.11
5 | !function(a){"function"==typeof define&&define.amd?define("jquery.validate.unobtrusive",["jquery-validation"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery-validation")):jQuery.validator.unobtrusive=a(jQuery)}(function(a){function e(a,e,n){a.rules[e]=n,a.message&&(a.messages[e]=a.message)}function n(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function t(a){return a.replace(/([!"#$%&'()*+,.\/:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function r(a){return a.substr(0,a.lastIndexOf(".")+1)}function i(a,e){return 0===a.indexOf("*.")&&(a=a.replace("*.",e)),a}function o(e,n){var r=a(this).find("[data-valmsg-for='"+t(n[0].name)+"']"),i=r.attr("data-valmsg-replace"),o=i?a.parseJSON(i)!==!1:null;r.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",r),o?(r.empty(),e.removeClass("input-validation-error").appendTo(r)):e.hide()}function d(e,n){var t=a(this).find("[data-valmsg-summary=true]"),r=t.find("ul");r&&r.length&&n.errorList.length&&(r.empty(),t.addClass("validation-summary-errors").removeClass("validation-summary-valid"),a.each(n.errorList,function(){a(" ").html(this.message).appendTo(r)}))}function s(e){var n=e.data("unobtrusiveContainer");if(n){var t=n.attr("data-valmsg-replace"),r=t?a.parseJSON(t):null;n.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),r&&n.empty()}}function l(e){var n=a(this),t="__jquery_unobtrusive_validation_form_reset";if(!n.data(t)){n.data(t,!0);try{n.data("validator").resetForm()}finally{n.removeData(t)}n.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),n.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function u(e){var n=a(e),t=n.data(v),r=a.proxy(l,e),i=f.unobtrusive.options||{},u=function(n,t){var r=i[n];r&&a.isFunction(r)&&r.apply(e,t)};return t||(t={options:{errorClass:i.errorClass||"input-validation-error",errorElement:i.errorElement||"span",errorPlacement:function(){o.apply(e,arguments),u("errorPlacement",arguments)},invalidHandler:function(){d.apply(e,arguments),u("invalidHandler",arguments)},messages:{},rules:{},success:function(){s.apply(e,arguments),u("success",arguments)}},attachValidation:function(){n.off("reset."+v,r).on("reset."+v,r).validate(this.options)},validate:function(){return n.validate(),n.valid()}},n.data(v,t)),t}var m,f=a.validator,v="unobtrusiveValidation";return f.unobtrusive={adapters:[],parseElement:function(e,n){var t,r,i,o=a(e),d=o.parents("form")[0];d&&(t=u(d),t.options.rules[e.name]=r={},t.options.messages[e.name]=i={},a.each(this.adapters,function(){var n="data-val-"+this.name,t=o.attr(n),s={};void 0!==t&&(n+="-",a.each(this.params,function(){s[this]=o.attr(n+this)}),this.adapt({element:e,form:d,message:t,params:s,rules:r,messages:i}))}),a.extend(r,{__dummy__:!0}),n||t.attachValidation())},parse:function(e){var n=a(e),t=n.parents().addBack().filter("form").add(n.find("form")).has("[data-val=true]");n.find("[data-val=true]").each(function(){f.unobtrusive.parseElement(this,!0)}),t.each(function(){var a=u(this);a&&a.attachValidation()})}},m=f.unobtrusive.adapters,m.add=function(a,e,n){return n||(n=e,e=[]),this.push({name:a,params:e,adapt:n}),this},m.addBool=function(a,n){return this.add(a,function(t){e(t,n||a,!0)})},m.addMinMax=function(a,n,t,r,i,o){return this.add(a,[i||"min",o||"max"],function(a){var i=a.params.min,o=a.params.max;i&&o?e(a,r,[i,o]):i?e(a,n,i):o&&e(a,t,o)})},m.addSingleVal=function(a,n,t){return this.add(a,[n||"val"],function(r){e(r,t||a,r.params[n])})},f.addMethod("__dummy__",function(a,e,n){return!0}),f.addMethod("regex",function(a,e,n){var t;return!!this.optional(e)||(t=new RegExp(n).exec(a),t&&0===t.index&&t[0].length===a.length)}),f.addMethod("nonalphamin",function(a,e,n){var t;return n&&(t=a.match(/\W/g),t=t&&t.length>=n),t}),f.methods.extension?(m.addSingleVal("accept","mimtype"),m.addSingleVal("extension","extension")):m.addSingleVal("extension","extension","accept"),m.addSingleVal("regex","pattern"),m.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),m.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),m.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),m.add("equalto",["other"],function(n){var o=r(n.element.name),d=n.params.other,s=i(d,o),l=a(n.form).find(":input").filter("[name='"+t(s)+"']")[0];e(n,"equalTo",l)}),m.add("required",function(a){"INPUT"===a.element.tagName.toUpperCase()&&"CHECKBOX"===a.element.type.toUpperCase()||e(a,"required",!0)}),m.add("remote",["url","type","additionalfields"],function(o){var d={url:o.params.url,type:o.params.type||"GET",data:{}},s=r(o.element.name);a.each(n(o.params.additionalfields||o.element.name),function(e,n){var r=i(n,s);d.data[r]=function(){var e=a(o.form).find(":input").filter("[name='"+t(r)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),e(o,"remote",d)}),m.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&e(a,"minlength",a.params.min),a.params.nonalphamin&&e(a,"nonalphamin",a.params.nonalphamin),a.params.regex&&e(a,"regex",a.params.regex)}),m.add("fileextensions",["extensions"],function(a){e(a,"extension",a.params.extensions)}),a(function(){f.unobtrusive.parse(document)}),f.unobtrusive});
--------------------------------------------------------------------------------
/Libraries/tests/GalaxyHotel.Infrastructure.IntegrationTests/Data/ReservationRepositoryTests.cs:
--------------------------------------------------------------------------------
1 | using GalaxyHotel.Core.Models;
2 | using GalaxyHotel.Core.Specification;
3 | using GalaxyHotel.Infrastructure.Data;
4 | using GalaxyHotel.Infrastructure.IntegrationTests.SetUp;
5 | using Microsoft.Data.Sqlite;
6 | using Microsoft.EntityFrameworkCore;
7 | using NUnit.Framework;
8 | using System;
9 | using System.Linq;
10 | using System.Threading.Tasks;
11 |
12 | namespace GalaxyHotel.Infrastructure.IntegrationTests
13 | {
14 | public class ReservationRepositoryTests
15 | {
16 | private SqliteConnection connection;
17 | private TestGalaxyHotelContext galaxyHotelContext;
18 | private ReservationRepository reservationRepository;
19 | private readonly DateTimeOffset checkInDate = DateTimeOffset.Now;
20 | private readonly DateTimeOffset checkOutDate = DateTimeOffset.Now.AddDays(1);
21 | private readonly int firstGuest = 1;
22 | private readonly int secondGuest = 2;
23 | private readonly int thirdGuest = 3;
24 | private readonly int secondRoom = 2;
25 | private readonly decimal pricePerNight = 10;
26 |
27 | [SetUp]
28 | public void Setup()
29 | {
30 | connection = new SqliteConnection("DataSource=:memory:;Foreign Keys=False");
31 | connection.Open();
32 |
33 | var options = new DbContextOptionsBuilder().UseSqlite(connection).Options;
34 | galaxyHotelContext = new TestGalaxyHotelContext(options);
35 | galaxyHotelContext.Database.EnsureCreated();
36 |
37 | reservationRepository = new ReservationRepository(galaxyHotelContext);
38 | }
39 |
40 | [TearDown]
41 | public void TearDown()
42 | {
43 | galaxyHotelContext.Dispose();
44 | connection.Close();
45 | }
46 |
47 | [Test]
48 | public async Task GetReservations_ReservationsExisting_ReservationsReturned()
49 | {
50 | var reservations = await reservationRepository.ListAllAsync();
51 |
52 | Assert.That(reservations.Count, Is.EqualTo(3));
53 | }
54 |
55 | [Test]
56 | public async Task CountReservations_ReservationsExisting_ReservationsNumberReturned()
57 | {
58 | var reservationCount = await reservationRepository
59 | .CountAsync(new GuestReservationSpecification(firstGuest));
60 |
61 | Assert.That(reservationCount, Is.EqualTo(1));
62 | }
63 |
64 | [Test]
65 | public async Task GetReservationById_ReservationExisting_ReservationReturned()
66 | {
67 | var reservations = await reservationRepository
68 | .ListAllAsync();
69 |
70 | var reservation = await reservationRepository
71 | .GetByIdAsync(reservations.First().Id);
72 |
73 | Assert.That(reservation, Is.Not.Null);
74 | }
75 |
76 | [Test]
77 | public async Task GetReservationByGuestId_ReservationExisting_ReservationReturned()
78 | {
79 | var reservations = await reservationRepository
80 | .ListAsync(new GuestReservationSpecification(firstGuest));
81 |
82 | Assert.That(reservations.Count, Is.EqualTo(1));
83 | }
84 |
85 | [Test]
86 | public async Task AddReservation_RoomAvailable_ReservationCreated()
87 | {
88 | var reservation = new Reservation
89 | {
90 | Id = 10,
91 | CheckIn = checkInDate,
92 | CheckOut = checkOutDate,
93 | GuestId = secondGuest,
94 | RoomId = secondRoom,
95 | TotalPrice = pricePerNight
96 | };
97 |
98 | await reservationRepository.AddAsync(reservation);
99 |
100 | var reservationCount = await reservationRepository
101 | .CountAsync(new GuestReservationSpecification(secondGuest));
102 | Assert.That(reservationCount, Is.EqualTo(2));
103 | }
104 |
105 | [Test]
106 | public async Task DeleteReservation_ReservationExisting_ReservationDeleted()
107 | {
108 | var reservation = new Reservation
109 | {
110 | Id = 11,
111 | CheckIn = checkInDate,
112 | CheckOut = checkOutDate,
113 | GuestId = thirdGuest,
114 | RoomId = secondRoom,
115 | TotalPrice = pricePerNight
116 | };
117 |
118 | await reservationRepository.AddAsync(reservation);
119 |
120 | await reservationRepository.DeleteAsync(reservation);
121 |
122 | var reservationCount = await reservationRepository
123 | .CountAsync(new GuestReservationSpecification(thirdGuest));
124 | Assert.That(reservationCount, Is.EqualTo(1));
125 | }
126 |
127 | [Test, Ignore("ignore")]
128 | public async Task UpdateReservation_ReservationExisting_ReservationUpdated()
129 | {
130 | var newPricePerNight = 50;
131 | var guestId = 1;
132 | var reservation = await reservationRepository
133 | .FirstAsync(new GuestReservationSpecification(guestId));
134 |
135 | await reservationRepository.UpdateAsync(reservation);
136 |
137 | reservation = await reservationRepository
138 | .FirstAsync(new GuestReservationSpecification(guestId));
139 |
140 | Assert.That(reservation.TotalPrice, Is.EqualTo(newPricePerNight));
141 | }
142 |
143 | [Test]
144 | public async Task GetReservationByGuestId_GuestNotExisting_NoReservationReturned()
145 | {
146 | var notExistingGuest = 5;
147 |
148 | var reservation = await reservationRepository
149 | .FirstOrDefaultAsync(new GuestReservationSpecification(notExistingGuest));
150 |
151 | Assert.That(reservation, Is.Null);
152 | }
153 | }
154 | }
--------------------------------------------------------------------------------
/Libraries/tests/GalaxyHotel.Infrastructure.IntegrationTests/SetUp/TestGalaxyHotelContext.cs:
--------------------------------------------------------------------------------
1 | using GalaxyHotel.Core.Models;
2 | using GalaxyHotel.Infrastructure.Data;
3 | using Microsoft.EntityFrameworkCore;
4 | using System;
5 |
6 | namespace GalaxyHotel.Infrastructure.IntegrationTests.SetUp
7 | {
8 | public class TestGalaxyHotelContext : GalaxyHotelContext
9 | {
10 | private static DateTimeOffset checkInDate = DateTimeOffset.Now;
11 | private static DateTimeOffset checkOutDate = DateTimeOffset.Now.AddDays(1);
12 |
13 | public TestGalaxyHotelContext(DbContextOptions options)
14 | : base(options)
15 | { }
16 |
17 | protected override void OnModelCreating(ModelBuilder modelBuilder)
18 | {
19 | base.OnModelCreating(modelBuilder);
20 |
21 | modelBuilder.Entity()
22 | .HasOne(r => r.Guest)
23 | .WithMany(r => r.Reservations)
24 | .OnDelete(DeleteBehavior.Cascade);
25 |
26 | modelBuilder.Entity()
27 | .HasOne(r => r.Room)
28 | .WithMany(r => r.Reservations)
29 | .OnDelete(DeleteBehavior.Cascade);
30 |
31 | modelBuilder.Entity()
32 | .HasMany(g => g.Reservations)
33 | .WithOne(g => g.Guest)
34 | .OnDelete(DeleteBehavior.Cascade);
35 |
36 | modelBuilder.Entity()
37 | .HasOne(g => g.Address)
38 | .WithOne(g => g.Guest)
39 | .OnDelete(DeleteBehavior.Cascade);
40 |
41 | modelBuilder.Entity()
42 | .HasOne(g => g.PaymentMethod)
43 | .WithOne(g => g.Guest)
44 | .OnDelete(DeleteBehavior.Cascade);
45 |
46 | modelBuilder.Entity()
47 | .HasOne(g => g.Guest)
48 | .WithOne(g => g.Address)
49 | .OnDelete(DeleteBehavior.Cascade);
50 |
51 | modelBuilder.Entity()
52 | .HasOne(g => g.Guest)
53 | .WithOne(g => g.PaymentMethod)
54 | .OnDelete(DeleteBehavior.Cascade);
55 |
56 | modelBuilder.Entity().HasData(
57 | new PaymentMethod { Id = 1, Alias = "Private Credit Card 1", CardNumber = "123412349876598765", DueDate = DateTimeOffset.Now.AddYears(1) },
58 | new PaymentMethod { Id = 2, Alias = "Private Credit Card 2", CardNumber = "123412349876598766", DueDate = DateTimeOffset.Now.AddYears(1) },
59 | new PaymentMethod { Id = 3, Alias = "Private Credit Card 3", CardNumber = "123412349876598767", DueDate = DateTimeOffset.Now.AddYears(1) },
60 | new PaymentMethod { Id = 4, Alias = "Private Credit Card 4", CardNumber = "123412349876598768", DueDate = DateTimeOffset.Now.AddYears(1) });
61 |
62 | modelBuilder.Entity().HasData(
63 | new Address { Id = 1, City = "New York", Street = "13th Space Road, Brooklyn", Country = "United States of America", State = "New York", ZipCode = "13021" },
64 | new Address { Id = 2, City = "Los Angeles", Street = "12th Hollywood road, Hollywood", Country = "United States of America", State = "New York", ZipCode = "33024" },
65 | new Address { Id = 3, City = "Naples", Street = "Piazza Plebiscito 1, Naples", Country = "Italy", State = "", ZipCode = "80100" },
66 | new Address { Id = 4, City = "London", Street = "14th Valley Road", Country = "United States of America", State = "New York", ZipCode = "WC2N 5DU" });
67 |
68 | modelBuilder.Entity().HasData(
69 | new Guest { Id = 1, FirstName = "John", LastName = "Doe", AddressId = 1, PaymentMethodId = 1 },
70 | new Guest { Id = 2, FirstName = "Alex", LastName = "Harvey", AddressId = 2, PaymentMethodId = 2 },
71 | new Guest { Id = 3, FirstName = "Antonio", LastName = "Esposito", AddressId = 3, PaymentMethodId = 3 },
72 | new Guest { Id = 4, FirstName = "Mark", LastName = "River", AddressId = 4, PaymentMethodId = 4 });
73 |
74 | modelBuilder.Entity().HasData(
75 | new Room
76 | {
77 | Id = 1,
78 | Floor = 1,
79 | Name = "Andromeda",
80 | PricePerNight = 200
81 | },
82 | new Room
83 | {
84 | Id = 2,
85 | Floor = 1,
86 | Name = "Whirpool",
87 | PricePerNight = 150
88 | },
89 | new Room
90 | {
91 | Id = 3,
92 | Floor = 1,
93 | Name = "Sombrero",
94 | PricePerNight = 100
95 | });
96 |
97 | modelBuilder.Entity().HasData(
98 | new Reservation
99 | {
100 | Id = 1,
101 | CheckIn = checkInDate,
102 | CheckOut = checkOutDate,
103 | GuestId = 1,
104 | RoomId = 1,
105 | TotalPrice = 200
106 | },
107 | new Reservation
108 | {
109 | Id = 2,
110 | CheckIn = checkInDate,
111 | CheckOut = checkOutDate,
112 | GuestId = 2,
113 | RoomId = 2,
114 | TotalPrice = 150
115 | },
116 | new Reservation
117 | {
118 | Id = 3,
119 | CheckIn = checkInDate,
120 | CheckOut = checkOutDate,
121 | GuestId = 3,
122 | RoomId = 3,
123 | TotalPrice = 100
124 |
125 | });
126 | }
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/Website/src/GalaxyHotel.Website/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Reboot v4.3.1 (https://getbootstrap.com/)
3 | * Copyright 2011-2019 The Bootstrap Authors
4 | * Copyright 2011-2019 Twitter, Inc.
5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
7 | */
8 | *,
9 | *::before,
10 | *::after {
11 | box-sizing: border-box;
12 | }
13 |
14 | html {
15 | font-family: sans-serif;
16 | line-height: 1.15;
17 | -webkit-text-size-adjust: 100%;
18 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
19 | }
20 |
21 | article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
22 | display: block;
23 | }
24 |
25 | body {
26 | margin: 0;
27 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
28 | font-size: 1rem;
29 | font-weight: 400;
30 | line-height: 1.5;
31 | color: #212529;
32 | text-align: left;
33 | background-color: #fff;
34 | }
35 |
36 | [tabindex="-1"]:focus {
37 | outline: 0 !important;
38 | }
39 |
40 | hr {
41 | box-sizing: content-box;
42 | height: 0;
43 | overflow: visible;
44 | }
45 |
46 | h1, h2, h3, h4, h5, h6 {
47 | margin-top: 0;
48 | margin-bottom: 0.5rem;
49 | }
50 |
51 | p {
52 | margin-top: 0;
53 | margin-bottom: 1rem;
54 | }
55 |
56 | abbr[title],
57 | abbr[data-original-title] {
58 | text-decoration: underline;
59 | -webkit-text-decoration: underline dotted;
60 | text-decoration: underline dotted;
61 | cursor: help;
62 | border-bottom: 0;
63 | -webkit-text-decoration-skip-ink: none;
64 | text-decoration-skip-ink: none;
65 | }
66 |
67 | address {
68 | margin-bottom: 1rem;
69 | font-style: normal;
70 | line-height: inherit;
71 | }
72 |
73 | ol,
74 | ul,
75 | dl {
76 | margin-top: 0;
77 | margin-bottom: 1rem;
78 | }
79 |
80 | ol ol,
81 | ul ul,
82 | ol ul,
83 | ul ol {
84 | margin-bottom: 0;
85 | }
86 |
87 | dt {
88 | font-weight: 700;
89 | }
90 |
91 | dd {
92 | margin-bottom: .5rem;
93 | margin-left: 0;
94 | }
95 |
96 | blockquote {
97 | margin: 0 0 1rem;
98 | }
99 |
100 | b,
101 | strong {
102 | font-weight: bolder;
103 | }
104 |
105 | small {
106 | font-size: 80%;
107 | }
108 |
109 | sub,
110 | sup {
111 | position: relative;
112 | font-size: 75%;
113 | line-height: 0;
114 | vertical-align: baseline;
115 | }
116 |
117 | sub {
118 | bottom: -.25em;
119 | }
120 |
121 | sup {
122 | top: -.5em;
123 | }
124 |
125 | a {
126 | color: #007bff;
127 | text-decoration: none;
128 | background-color: transparent;
129 | }
130 |
131 | a:hover {
132 | color: #0056b3;
133 | text-decoration: underline;
134 | }
135 |
136 | a:not([href]):not([tabindex]) {
137 | color: inherit;
138 | text-decoration: none;
139 | }
140 |
141 | a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
142 | color: inherit;
143 | text-decoration: none;
144 | }
145 |
146 | a:not([href]):not([tabindex]):focus {
147 | outline: 0;
148 | }
149 |
150 | pre,
151 | code,
152 | kbd,
153 | samp {
154 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
155 | font-size: 1em;
156 | }
157 |
158 | pre {
159 | margin-top: 0;
160 | margin-bottom: 1rem;
161 | overflow: auto;
162 | }
163 |
164 | figure {
165 | margin: 0 0 1rem;
166 | }
167 |
168 | img {
169 | vertical-align: middle;
170 | border-style: none;
171 | }
172 |
173 | svg {
174 | overflow: hidden;
175 | vertical-align: middle;
176 | }
177 |
178 | table {
179 | border-collapse: collapse;
180 | }
181 |
182 | caption {
183 | padding-top: 0.75rem;
184 | padding-bottom: 0.75rem;
185 | color: #6c757d;
186 | text-align: left;
187 | caption-side: bottom;
188 | }
189 |
190 | th {
191 | text-align: inherit;
192 | }
193 |
194 | label {
195 | display: inline-block;
196 | margin-bottom: 0.5rem;
197 | }
198 |
199 | button {
200 | border-radius: 0;
201 | }
202 |
203 | button:focus {
204 | outline: 1px dotted;
205 | outline: 5px auto -webkit-focus-ring-color;
206 | }
207 |
208 | input,
209 | button,
210 | select,
211 | optgroup,
212 | textarea {
213 | margin: 0;
214 | font-family: inherit;
215 | font-size: inherit;
216 | line-height: inherit;
217 | }
218 |
219 | button,
220 | input {
221 | overflow: visible;
222 | }
223 |
224 | button,
225 | select {
226 | text-transform: none;
227 | }
228 |
229 | select {
230 | word-wrap: normal;
231 | }
232 |
233 | button,
234 | [type="button"],
235 | [type="reset"],
236 | [type="submit"] {
237 | -webkit-appearance: button;
238 | }
239 |
240 | button:not(:disabled),
241 | [type="button"]:not(:disabled),
242 | [type="reset"]:not(:disabled),
243 | [type="submit"]:not(:disabled) {
244 | cursor: pointer;
245 | }
246 |
247 | button::-moz-focus-inner,
248 | [type="button"]::-moz-focus-inner,
249 | [type="reset"]::-moz-focus-inner,
250 | [type="submit"]::-moz-focus-inner {
251 | padding: 0;
252 | border-style: none;
253 | }
254 |
255 | input[type="radio"],
256 | input[type="checkbox"] {
257 | box-sizing: border-box;
258 | padding: 0;
259 | }
260 |
261 | input[type="date"],
262 | input[type="time"],
263 | input[type="datetime-local"],
264 | input[type="month"] {
265 | -webkit-appearance: listbox;
266 | }
267 |
268 | textarea {
269 | overflow: auto;
270 | resize: vertical;
271 | }
272 |
273 | fieldset {
274 | min-width: 0;
275 | padding: 0;
276 | margin: 0;
277 | border: 0;
278 | }
279 |
280 | legend {
281 | display: block;
282 | width: 100%;
283 | max-width: 100%;
284 | padding: 0;
285 | margin-bottom: .5rem;
286 | font-size: 1.5rem;
287 | line-height: inherit;
288 | color: inherit;
289 | white-space: normal;
290 | }
291 |
292 | progress {
293 | vertical-align: baseline;
294 | }
295 |
296 | [type="number"]::-webkit-inner-spin-button,
297 | [type="number"]::-webkit-outer-spin-button {
298 | height: auto;
299 | }
300 |
301 | [type="search"] {
302 | outline-offset: -2px;
303 | -webkit-appearance: none;
304 | }
305 |
306 | [type="search"]::-webkit-search-decoration {
307 | -webkit-appearance: none;
308 | }
309 |
310 | ::-webkit-file-upload-button {
311 | font: inherit;
312 | -webkit-appearance: button;
313 | }
314 |
315 | output {
316 | display: inline-block;
317 | }
318 |
319 | summary {
320 | display: list-item;
321 | cursor: pointer;
322 | }
323 |
324 | template {
325 | display: none;
326 | }
327 |
328 | [hidden] {
329 | display: none !important;
330 | }
331 | /*# sourceMappingURL=bootstrap-reboot.css.map */
--------------------------------------------------------------------------------
/Helpdesk/src/GalaxyHotel.HelpDesk/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Reboot v4.3.1 (https://getbootstrap.com/)
3 | * Copyright 2011-2019 The Bootstrap Authors
4 | * Copyright 2011-2019 Twitter, Inc.
5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
7 | */
8 | *,
9 | *::before,
10 | *::after {
11 | box-sizing: border-box;
12 | }
13 |
14 | html {
15 | font-family: sans-serif;
16 | line-height: 1.15;
17 | -webkit-text-size-adjust: 100%;
18 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
19 | }
20 |
21 | article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
22 | display: block;
23 | }
24 |
25 | body {
26 | margin: 0;
27 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
28 | font-size: 1rem;
29 | font-weight: 400;
30 | line-height: 1.5;
31 | color: #212529;
32 | text-align: left;
33 | background-color: #fff;
34 | }
35 |
36 | [tabindex="-1"]:focus {
37 | outline: 0 !important;
38 | }
39 |
40 | hr {
41 | box-sizing: content-box;
42 | height: 0;
43 | overflow: visible;
44 | }
45 |
46 | h1, h2, h3, h4, h5, h6 {
47 | margin-top: 0;
48 | margin-bottom: 0.5rem;
49 | }
50 |
51 | p {
52 | margin-top: 0;
53 | margin-bottom: 1rem;
54 | }
55 |
56 | abbr[title],
57 | abbr[data-original-title] {
58 | text-decoration: underline;
59 | -webkit-text-decoration: underline dotted;
60 | text-decoration: underline dotted;
61 | cursor: help;
62 | border-bottom: 0;
63 | -webkit-text-decoration-skip-ink: none;
64 | text-decoration-skip-ink: none;
65 | }
66 |
67 | address {
68 | margin-bottom: 1rem;
69 | font-style: normal;
70 | line-height: inherit;
71 | }
72 |
73 | ol,
74 | ul,
75 | dl {
76 | margin-top: 0;
77 | margin-bottom: 1rem;
78 | }
79 |
80 | ol ol,
81 | ul ul,
82 | ol ul,
83 | ul ol {
84 | margin-bottom: 0;
85 | }
86 |
87 | dt {
88 | font-weight: 700;
89 | }
90 |
91 | dd {
92 | margin-bottom: .5rem;
93 | margin-left: 0;
94 | }
95 |
96 | blockquote {
97 | margin: 0 0 1rem;
98 | }
99 |
100 | b,
101 | strong {
102 | font-weight: bolder;
103 | }
104 |
105 | small {
106 | font-size: 80%;
107 | }
108 |
109 | sub,
110 | sup {
111 | position: relative;
112 | font-size: 75%;
113 | line-height: 0;
114 | vertical-align: baseline;
115 | }
116 |
117 | sub {
118 | bottom: -.25em;
119 | }
120 |
121 | sup {
122 | top: -.5em;
123 | }
124 |
125 | a {
126 | color: #007bff;
127 | text-decoration: none;
128 | background-color: transparent;
129 | }
130 |
131 | a:hover {
132 | color: #0056b3;
133 | text-decoration: underline;
134 | }
135 |
136 | a:not([href]):not([tabindex]) {
137 | color: inherit;
138 | text-decoration: none;
139 | }
140 |
141 | a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
142 | color: inherit;
143 | text-decoration: none;
144 | }
145 |
146 | a:not([href]):not([tabindex]):focus {
147 | outline: 0;
148 | }
149 |
150 | pre,
151 | code,
152 | kbd,
153 | samp {
154 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
155 | font-size: 1em;
156 | }
157 |
158 | pre {
159 | margin-top: 0;
160 | margin-bottom: 1rem;
161 | overflow: auto;
162 | }
163 |
164 | figure {
165 | margin: 0 0 1rem;
166 | }
167 |
168 | img {
169 | vertical-align: middle;
170 | border-style: none;
171 | }
172 |
173 | svg {
174 | overflow: hidden;
175 | vertical-align: middle;
176 | }
177 |
178 | table {
179 | border-collapse: collapse;
180 | }
181 |
182 | caption {
183 | padding-top: 0.75rem;
184 | padding-bottom: 0.75rem;
185 | color: #6c757d;
186 | text-align: left;
187 | caption-side: bottom;
188 | }
189 |
190 | th {
191 | text-align: inherit;
192 | }
193 |
194 | label {
195 | display: inline-block;
196 | margin-bottom: 0.5rem;
197 | }
198 |
199 | button {
200 | border-radius: 0;
201 | }
202 |
203 | button:focus {
204 | outline: 1px dotted;
205 | outline: 5px auto -webkit-focus-ring-color;
206 | }
207 |
208 | input,
209 | button,
210 | select,
211 | optgroup,
212 | textarea {
213 | margin: 0;
214 | font-family: inherit;
215 | font-size: inherit;
216 | line-height: inherit;
217 | }
218 |
219 | button,
220 | input {
221 | overflow: visible;
222 | }
223 |
224 | button,
225 | select {
226 | text-transform: none;
227 | }
228 |
229 | select {
230 | word-wrap: normal;
231 | }
232 |
233 | button,
234 | [type="button"],
235 | [type="reset"],
236 | [type="submit"] {
237 | -webkit-appearance: button;
238 | }
239 |
240 | button:not(:disabled),
241 | [type="button"]:not(:disabled),
242 | [type="reset"]:not(:disabled),
243 | [type="submit"]:not(:disabled) {
244 | cursor: pointer;
245 | }
246 |
247 | button::-moz-focus-inner,
248 | [type="button"]::-moz-focus-inner,
249 | [type="reset"]::-moz-focus-inner,
250 | [type="submit"]::-moz-focus-inner {
251 | padding: 0;
252 | border-style: none;
253 | }
254 |
255 | input[type="radio"],
256 | input[type="checkbox"] {
257 | box-sizing: border-box;
258 | padding: 0;
259 | }
260 |
261 | input[type="date"],
262 | input[type="time"],
263 | input[type="datetime-local"],
264 | input[type="month"] {
265 | -webkit-appearance: listbox;
266 | }
267 |
268 | textarea {
269 | overflow: auto;
270 | resize: vertical;
271 | }
272 |
273 | fieldset {
274 | min-width: 0;
275 | padding: 0;
276 | margin: 0;
277 | border: 0;
278 | }
279 |
280 | legend {
281 | display: block;
282 | width: 100%;
283 | max-width: 100%;
284 | padding: 0;
285 | margin-bottom: .5rem;
286 | font-size: 1.5rem;
287 | line-height: inherit;
288 | color: inherit;
289 | white-space: normal;
290 | }
291 |
292 | progress {
293 | vertical-align: baseline;
294 | }
295 |
296 | [type="number"]::-webkit-inner-spin-button,
297 | [type="number"]::-webkit-outer-spin-button {
298 | height: auto;
299 | }
300 |
301 | [type="search"] {
302 | outline-offset: -2px;
303 | -webkit-appearance: none;
304 | }
305 |
306 | [type="search"]::-webkit-search-decoration {
307 | -webkit-appearance: none;
308 | }
309 |
310 | ::-webkit-file-upload-button {
311 | font: inherit;
312 | -webkit-appearance: button;
313 | }
314 |
315 | output {
316 | display: inline-block;
317 | }
318 |
319 | summary {
320 | display: list-item;
321 | cursor: pointer;
322 | }
323 |
324 | template {
325 | display: none;
326 | }
327 |
328 | [hidden] {
329 | display: none !important;
330 | }
331 | /*# sourceMappingURL=bootstrap-reboot.css.map */
--------------------------------------------------------------------------------
/Helpdesk/src/GalaxyHotel.HelpDesk/Migrations/GalaxyHotelContextModelSnapshot.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using GalaxyHotel.Infrastructure.Data;
4 | using Microsoft.EntityFrameworkCore;
5 | using Microsoft.EntityFrameworkCore.Infrastructure;
6 | using Microsoft.EntityFrameworkCore.Metadata;
7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8 |
9 | namespace GalaxyHotel.HelpDesk.Migrations
10 | {
11 | [DbContext(typeof(GalaxyHotelContext))]
12 | partial class GalaxyHotelContextModelSnapshot : ModelSnapshot
13 | {
14 | protected override void BuildModel(ModelBuilder modelBuilder)
15 | {
16 | #pragma warning disable 612, 618
17 | modelBuilder
18 | .HasAnnotation("ProductVersion", "3.1.5")
19 | .HasAnnotation("Relational:MaxIdentifierLength", 128)
20 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
21 |
22 | modelBuilder.Entity("GalaxyHotel.Core.Models.Address", b =>
23 | {
24 | b.Property("Id")
25 | .ValueGeneratedOnAdd()
26 | .HasColumnType("int")
27 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
28 |
29 | b.Property("City")
30 | .HasColumnType("nvarchar(max)");
31 |
32 | b.Property("Country")
33 | .HasColumnType("nvarchar(max)");
34 |
35 | b.Property("State")
36 | .HasColumnType("nvarchar(max)");
37 |
38 | b.Property("Street")
39 | .HasColumnType("nvarchar(max)");
40 |
41 | b.Property("ZipCode")
42 | .HasColumnType("nvarchar(max)");
43 |
44 | b.HasKey("Id");
45 |
46 | b.ToTable("Addresses");
47 | });
48 |
49 | modelBuilder.Entity("GalaxyHotel.Core.Models.Guest", b =>
50 | {
51 | b.Property("Id")
52 | .ValueGeneratedOnAdd()
53 | .HasColumnType("int")
54 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
55 |
56 | b.Property("AddressId")
57 | .HasColumnType("int");
58 |
59 | b.Property("FirstName")
60 | .HasColumnType("nvarchar(max)");
61 |
62 | b.Property("LastName")
63 | .HasColumnType("nvarchar(max)");
64 |
65 | b.Property("PaymentMethodId")
66 | .HasColumnType("int");
67 |
68 | b.HasKey("Id");
69 |
70 | b.HasIndex("AddressId")
71 | .IsUnique();
72 |
73 | b.HasIndex("PaymentMethodId")
74 | .IsUnique();
75 |
76 | b.ToTable("Guests");
77 | });
78 |
79 | modelBuilder.Entity("GalaxyHotel.Core.Models.PaymentMethod", b =>
80 | {
81 | b.Property("Id")
82 | .ValueGeneratedOnAdd()
83 | .HasColumnType("int")
84 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
85 |
86 | b.Property("Alias")
87 | .HasColumnType("nvarchar(max)");
88 |
89 | b.Property("CardNumber")
90 | .HasColumnType("nvarchar(max)");
91 |
92 | b.Property("DueDate")
93 | .HasColumnType("datetimeoffset");
94 |
95 | b.HasKey("Id");
96 |
97 | b.ToTable("PaymentMethods");
98 | });
99 |
100 | modelBuilder.Entity("GalaxyHotel.Core.Models.Reservation", b =>
101 | {
102 | b.Property("Id")
103 | .ValueGeneratedOnAdd()
104 | .HasColumnType("int")
105 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
106 |
107 | b.Property("CheckIn")
108 | .HasColumnType("datetimeoffset");
109 |
110 | b.Property("CheckOut")
111 | .HasColumnType("datetimeoffset");
112 |
113 | b.Property("GuestId")
114 | .HasColumnType("int");
115 |
116 | b.Property("RoomId")
117 | .HasColumnType("int");
118 |
119 | b.Property("TotalPrice")
120 | .HasColumnType("decimal(18,2)");
121 |
122 | b.HasKey("Id");
123 |
124 | b.HasIndex("GuestId");
125 |
126 | b.HasIndex("RoomId");
127 |
128 | b.ToTable("Reservations");
129 | });
130 |
131 | modelBuilder.Entity("GalaxyHotel.Core.Models.Room", b =>
132 | {
133 | b.Property("Id")
134 | .ValueGeneratedOnAdd()
135 | .HasColumnType("int")
136 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
137 |
138 | b.Property("Floor")
139 | .HasColumnType("int");
140 |
141 | b.Property("Name")
142 | .HasColumnType("nvarchar(max)");
143 |
144 | b.Property("PricePerNight")
145 | .HasColumnType("decimal(18,2)");
146 |
147 | b.HasKey("Id");
148 |
149 | b.ToTable("Rooms");
150 | });
151 |
152 | modelBuilder.Entity("GalaxyHotel.Core.Models.Guest", b =>
153 | {
154 | b.HasOne("GalaxyHotel.Core.Models.Address", "Address")
155 | .WithOne("Guest")
156 | .HasForeignKey("GalaxyHotel.Core.Models.Guest", "AddressId")
157 | .OnDelete(DeleteBehavior.Cascade)
158 | .IsRequired();
159 |
160 | b.HasOne("GalaxyHotel.Core.Models.PaymentMethod", "PaymentMethod")
161 | .WithOne("Guest")
162 | .HasForeignKey("GalaxyHotel.Core.Models.Guest", "PaymentMethodId")
163 | .OnDelete(DeleteBehavior.Cascade)
164 | .IsRequired();
165 | });
166 |
167 | modelBuilder.Entity("GalaxyHotel.Core.Models.Reservation", b =>
168 | {
169 | b.HasOne("GalaxyHotel.Core.Models.Guest", "Guest")
170 | .WithMany("Reservations")
171 | .HasForeignKey("GuestId")
172 | .OnDelete(DeleteBehavior.Cascade)
173 | .IsRequired();
174 |
175 | b.HasOne("GalaxyHotel.Core.Models.Room", "Room")
176 | .WithMany("Reservations")
177 | .HasForeignKey("RoomId")
178 | .OnDelete(DeleteBehavior.Cascade)
179 | .IsRequired();
180 | });
181 | #pragma warning restore 612, 618
182 | }
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/Helpdesk/src/GalaxyHotel.HelpDesk/Migrations/20200829211346_SeedGalaxyHotelDb.Designer.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using GalaxyHotel.Infrastructure.Data;
4 | using Microsoft.EntityFrameworkCore;
5 | using Microsoft.EntityFrameworkCore.Infrastructure;
6 | using Microsoft.EntityFrameworkCore.Metadata;
7 | using Microsoft.EntityFrameworkCore.Migrations;
8 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
9 |
10 | namespace GalaxyHotel.HelpDesk.Migrations
11 | {
12 | [DbContext(typeof(GalaxyHotelContext))]
13 | [Migration("20200829211346_SeedGalaxyHotelDb")]
14 | partial class SeedGalaxyHotelDb
15 | {
16 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
17 | {
18 | #pragma warning disable 612, 618
19 | modelBuilder
20 | .HasAnnotation("ProductVersion", "3.1.5")
21 | .HasAnnotation("Relational:MaxIdentifierLength", 128)
22 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
23 |
24 | modelBuilder.Entity("GalaxyHotel.Core.Models.Address", b =>
25 | {
26 | b.Property("Id")
27 | .ValueGeneratedOnAdd()
28 | .HasColumnType("int")
29 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
30 |
31 | b.Property("City")
32 | .HasColumnType("nvarchar(max)");
33 |
34 | b.Property("Country")
35 | .HasColumnType("nvarchar(max)");
36 |
37 | b.Property("State")
38 | .HasColumnType("nvarchar(max)");
39 |
40 | b.Property("Street")
41 | .HasColumnType("nvarchar(max)");
42 |
43 | b.Property("ZipCode")
44 | .HasColumnType("nvarchar(max)");
45 |
46 | b.HasKey("Id");
47 |
48 | b.ToTable("Addresses");
49 | });
50 |
51 | modelBuilder.Entity("GalaxyHotel.Core.Models.Guest", b =>
52 | {
53 | b.Property("Id")
54 | .ValueGeneratedOnAdd()
55 | .HasColumnType("int")
56 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
57 |
58 | b.Property("AddressId")
59 | .HasColumnType("int");
60 |
61 | b.Property("FirstName")
62 | .HasColumnType("nvarchar(max)");
63 |
64 | b.Property("LastName")
65 | .HasColumnType("nvarchar(max)");
66 |
67 | b.Property("PaymentMethodId")
68 | .HasColumnType("int");
69 |
70 | b.HasKey("Id");
71 |
72 | b.HasIndex("AddressId")
73 | .IsUnique();
74 |
75 | b.HasIndex("PaymentMethodId")
76 | .IsUnique();
77 |
78 | b.ToTable("Guests");
79 | });
80 |
81 | modelBuilder.Entity("GalaxyHotel.Core.Models.PaymentMethod", b =>
82 | {
83 | b.Property("Id")
84 | .ValueGeneratedOnAdd()
85 | .HasColumnType("int")
86 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
87 |
88 | b.Property("Alias")
89 | .HasColumnType("nvarchar(max)");
90 |
91 | b.Property("CardNumber")
92 | .HasColumnType("nvarchar(max)");
93 |
94 | b.Property("DueDate")
95 | .HasColumnType("datetimeoffset");
96 |
97 | b.HasKey("Id");
98 |
99 | b.ToTable("PaymentMethods");
100 | });
101 |
102 | modelBuilder.Entity("GalaxyHotel.Core.Models.Reservation", b =>
103 | {
104 | b.Property("Id")
105 | .ValueGeneratedOnAdd()
106 | .HasColumnType("int")
107 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
108 |
109 | b.Property("CheckIn")
110 | .HasColumnType("datetimeoffset");
111 |
112 | b.Property("CheckOut")
113 | .HasColumnType("datetimeoffset");
114 |
115 | b.Property("GuestId")
116 | .HasColumnType("int");
117 |
118 | b.Property("RoomId")
119 | .HasColumnType("int");
120 |
121 | b.Property("TotalPrice")
122 | .HasColumnType("decimal(18,2)");
123 |
124 | b.HasKey("Id");
125 |
126 | b.HasIndex("GuestId");
127 |
128 | b.HasIndex("RoomId");
129 |
130 | b.ToTable("Reservations");
131 | });
132 |
133 | modelBuilder.Entity("GalaxyHotel.Core.Models.Room", b =>
134 | {
135 | b.Property("Id")
136 | .ValueGeneratedOnAdd()
137 | .HasColumnType("int")
138 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
139 |
140 | b.Property("Floor")
141 | .HasColumnType("int");
142 |
143 | b.Property("Name")
144 | .HasColumnType("nvarchar(max)");
145 |
146 | b.Property("PricePerNight")
147 | .HasColumnType("decimal(18,2)");
148 |
149 | b.HasKey("Id");
150 |
151 | b.ToTable("Rooms");
152 | });
153 |
154 | modelBuilder.Entity("GalaxyHotel.Core.Models.Guest", b =>
155 | {
156 | b.HasOne("GalaxyHotel.Core.Models.Address", "Address")
157 | .WithOne("Guest")
158 | .HasForeignKey("GalaxyHotel.Core.Models.Guest", "AddressId")
159 | .OnDelete(DeleteBehavior.Cascade)
160 | .IsRequired();
161 |
162 | b.HasOne("GalaxyHotel.Core.Models.PaymentMethod", "PaymentMethod")
163 | .WithOne("Guest")
164 | .HasForeignKey("GalaxyHotel.Core.Models.Guest", "PaymentMethodId")
165 | .OnDelete(DeleteBehavior.Cascade)
166 | .IsRequired();
167 | });
168 |
169 | modelBuilder.Entity("GalaxyHotel.Core.Models.Reservation", b =>
170 | {
171 | b.HasOne("GalaxyHotel.Core.Models.Guest", "Guest")
172 | .WithMany("Reservations")
173 | .HasForeignKey("GuestId")
174 | .OnDelete(DeleteBehavior.Cascade)
175 | .IsRequired();
176 |
177 | b.HasOne("GalaxyHotel.Core.Models.Room", "Room")
178 | .WithMany("Reservations")
179 | .HasForeignKey("RoomId")
180 | .OnDelete(DeleteBehavior.Cascade)
181 | .IsRequired();
182 | });
183 | #pragma warning restore 612, 618
184 | }
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/Website/src/GalaxyHotel.Website/Data/Migrations/00000000000000_SeedGalaxyHotelDb.Designer.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using GalaxyHotel.Infrastructure.Data;
4 | using Microsoft.EntityFrameworkCore;
5 | using Microsoft.EntityFrameworkCore.Infrastructure;
6 | using Microsoft.EntityFrameworkCore.Metadata;
7 | using Microsoft.EntityFrameworkCore.Migrations;
8 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
9 |
10 | namespace GalaxyHotel.Website.Migrations
11 | {
12 | [DbContext(typeof(GalaxyHotelContext))]
13 | [Migration("20200828091845_SeedGalaxyHotelDb")]
14 | partial class SeedGalaxyHotelDb
15 | {
16 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
17 | {
18 | #pragma warning disable 612, 618
19 | modelBuilder
20 | .HasAnnotation("ProductVersion", "3.1.6")
21 | .HasAnnotation("Relational:MaxIdentifierLength", 128)
22 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
23 |
24 | modelBuilder.Entity("GalaxyHotel.Core.Models.Address", b =>
25 | {
26 | b.Property("Id")
27 | .ValueGeneratedOnAdd()
28 | .HasColumnType("int")
29 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
30 |
31 | b.Property("City")
32 | .HasColumnType("nvarchar(max)");
33 |
34 | b.Property("Country")
35 | .HasColumnType("nvarchar(max)");
36 |
37 | b.Property("State")
38 | .HasColumnType("nvarchar(max)");
39 |
40 | b.Property("Street")
41 | .HasColumnType("nvarchar(max)");
42 |
43 | b.Property("ZipCode")
44 | .HasColumnType("nvarchar(max)");
45 |
46 | b.HasKey("Id");
47 |
48 | b.ToTable("Addresses");
49 | });
50 |
51 | modelBuilder.Entity("GalaxyHotel.Core.Models.Guest", b =>
52 | {
53 | b.Property("Id")
54 | .ValueGeneratedOnAdd()
55 | .HasColumnType("int")
56 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
57 |
58 | b.Property("AddressId")
59 | .HasColumnType("int");
60 |
61 | b.Property("FirstName")
62 | .HasColumnType("nvarchar(max)");
63 |
64 | b.Property("LastName")
65 | .HasColumnType("nvarchar(max)");
66 |
67 | b.Property("PaymentMethodId")
68 | .HasColumnType("int");
69 |
70 | b.HasKey("Id");
71 |
72 | b.HasIndex("AddressId")
73 | .IsUnique();
74 |
75 | b.HasIndex("PaymentMethodId")
76 | .IsUnique();
77 |
78 | b.ToTable("Guests");
79 | });
80 |
81 | modelBuilder.Entity("GalaxyHotel.Core.Models.PaymentMethod", b =>
82 | {
83 | b.Property("Id")
84 | .ValueGeneratedOnAdd()
85 | .HasColumnType("int")
86 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
87 |
88 | b.Property("Alias")
89 | .HasColumnType("nvarchar(max)");
90 |
91 | b.Property("CardNumber")
92 | .HasColumnType("nvarchar(max)");
93 |
94 | b.Property("DueDate")
95 | .HasColumnType("datetimeoffset");
96 |
97 | b.HasKey("Id");
98 |
99 | b.ToTable("PaymentMethods");
100 | });
101 |
102 | modelBuilder.Entity("GalaxyHotel.Core.Models.Reservation", b =>
103 | {
104 | b.Property("Id")
105 | .ValueGeneratedOnAdd()
106 | .HasColumnType("int")
107 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
108 |
109 | b.Property("CheckIn")
110 | .HasColumnType("datetimeoffset");
111 |
112 | b.Property