Init
							
								
								
									
										49
									
								
								drinkMe.sln
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,49 @@
 | 
			
		||||
 | 
			
		||||
Microsoft Visual Studio Solution File, Format Version 12.00
 | 
			
		||||
# Visual Studio Version 16
 | 
			
		||||
VisualStudioVersion = 16.0.31129.286
 | 
			
		||||
MinimumVisualStudioVersion = 10.0.40219.1
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "drinkMe.Server", "drinkMe\Server\drinkMe.Server.csproj", "{4347ED6F-C6EC-44D2-A718-68490CBFFDF0}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "drinkMe.Client", "drinkMe\Client\drinkMe.Client.csproj", "{33E45585-7F5D-428D-8A27-FAB29A1A2E61}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "drinkMe.Shared", "drinkMe\Shared\drinkMe.Shared.csproj", "{85CCE095-22AC-434E-B77C-75998FF8DAD3}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "drinkMe.Client.Models", "drinkMe\drinkMe.Client.Models\drinkMe.Client.Models.csproj", "{7C00959D-3C7F-4617-B1DA-7909E5879C08}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "drinkMe.Server.Models", "drinkMe\drinkMe.Server.Models\drinkMe.Server.Models.csproj", "{EC73DC9D-FD40-4505-989D-83B49018401D}"
 | 
			
		||||
EndProject
 | 
			
		||||
Global
 | 
			
		||||
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
			
		||||
		Debug|Any CPU = Debug|Any CPU
 | 
			
		||||
		Release|Any CPU = Release|Any CPU
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
 | 
			
		||||
		{4347ED6F-C6EC-44D2-A718-68490CBFFDF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{4347ED6F-C6EC-44D2-A718-68490CBFFDF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{4347ED6F-C6EC-44D2-A718-68490CBFFDF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{4347ED6F-C6EC-44D2-A718-68490CBFFDF0}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{33E45585-7F5D-428D-8A27-FAB29A1A2E61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{33E45585-7F5D-428D-8A27-FAB29A1A2E61}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{33E45585-7F5D-428D-8A27-FAB29A1A2E61}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{33E45585-7F5D-428D-8A27-FAB29A1A2E61}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{85CCE095-22AC-434E-B77C-75998FF8DAD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{85CCE095-22AC-434E-B77C-75998FF8DAD3}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{85CCE095-22AC-434E-B77C-75998FF8DAD3}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{85CCE095-22AC-434E-B77C-75998FF8DAD3}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{7C00959D-3C7F-4617-B1DA-7909E5879C08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{7C00959D-3C7F-4617-B1DA-7909E5879C08}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{7C00959D-3C7F-4617-B1DA-7909E5879C08}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{7C00959D-3C7F-4617-B1DA-7909E5879C08}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{EC73DC9D-FD40-4505-989D-83B49018401D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{EC73DC9D-FD40-4505-989D-83B49018401D}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{EC73DC9D-FD40-4505-989D-83B49018401D}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{EC73DC9D-FD40-4505-989D-83B49018401D}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(SolutionProperties) = preSolution
 | 
			
		||||
		HideSolutionNode = FALSE
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(ExtensibilityGlobals) = postSolution
 | 
			
		||||
		SolutionGuid = {AB58BAA9-F7FC-4BF0-BC33-174BAB7F8320}
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
EndGlobal
 | 
			
		||||
							
								
								
									
										10
									
								
								drinkMe/Client/App.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,10 @@
 | 
			
		||||
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
 | 
			
		||||
    <Found Context="routeData">
 | 
			
		||||
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
 | 
			
		||||
    </Found>
 | 
			
		||||
    <NotFound>
 | 
			
		||||
        <LayoutView Layout="@typeof(MainLayout)">
 | 
			
		||||
            <p>Sorry, there's nothing at this address.</p>
 | 
			
		||||
        </LayoutView>
 | 
			
		||||
    </NotFound>
 | 
			
		||||
</Router>
 | 
			
		||||
							
								
								
									
										35
									
								
								drinkMe/Client/Components/PurchaseSteps.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,35 @@
 | 
			
		||||
<section class="section has-content-centered">
 | 
			
		||||
	<ul class="steps is-centered has-content-centered is-medium">
 | 
			
		||||
		<li class="steps-segment @VUtilities.IfTrueThen(PurchaseStep == PurchaseStep.ChoosingProducts, ActiveClass, InactiveClass)">
 | 
			
		||||
			<span class="steps-marker is-primary">
 | 
			
		||||
				<i class="fad fa-cart-plus"></i>
 | 
			
		||||
			</span>
 | 
			
		||||
			<div class="steps-content">
 | 
			
		||||
				<p class="is-size-4">Choose drinks</p>
 | 
			
		||||
			</div>
 | 
			
		||||
		</li>
 | 
			
		||||
		<li class="steps-segment @VUtilities.IfTrueThen(PurchaseStep == PurchaseStep.PaymentSetup, ActiveClass, InactiveClass)">
 | 
			
		||||
			<span class="steps-marker @VUtilities.IfTrueThen(PurchaseStep is PurchaseStep.PaymentSetup or PurchaseStep.SuccessfulPurchase, PrimaryClass)">
 | 
			
		||||
				<i class="fad fa-money-bill-wave"></i>
 | 
			
		||||
			</span>
 | 
			
		||||
			<div class="steps-content">
 | 
			
		||||
				<p class="is-size-4">Payment</p>
 | 
			
		||||
			</div>
 | 
			
		||||
		</li>
 | 
			
		||||
		<li class="steps-segment @VUtilities.IfTrueThen(PurchaseStep == PurchaseStep.SuccessfulPurchase, ActiveClass, InactiveClass)">
 | 
			
		||||
			<span class="steps-marker @VUtilities.IfTrueThen(PurchaseStep == PurchaseStep.SuccessfulPurchase, PrimaryClass)">
 | 
			
		||||
				<i class="fad fa-check-double"></i>
 | 
			
		||||
			</span>
 | 
			
		||||
			<div class="steps-content">
 | 
			
		||||
				<p class="is-size-4">Done!</p>
 | 
			
		||||
			</div>
 | 
			
		||||
		</li>
 | 
			
		||||
	</ul>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
	[Parameter] public PurchaseStep PurchaseStep { get; set; }
 | 
			
		||||
	const string ActiveClass = "is-active has-gaps";
 | 
			
		||||
	const string InactiveClass = "is-primary has-gaps";
 | 
			
		||||
	const string PrimaryClass = "is-primary";
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								drinkMe/Client/Helpers/VUtilities.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,13 @@
 | 
			
		||||
namespace drinkMe.Client.Helpers
 | 
			
		||||
{
 | 
			
		||||
	public static class VUtilities
 | 
			
		||||
	{
 | 
			
		||||
		public static T IfTrueThen<T>(bool conditionIsTrue, T ifTrue, T ifFalse = default)
 | 
			
		||||
		{
 | 
			
		||||
			if (conditionIsTrue)
 | 
			
		||||
				return ifTrue;
 | 
			
		||||
			else
 | 
			
		||||
				return ifFalse;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								drinkMe/Client/Pages/Done.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,23 @@
 | 
			
		||||
@page "/done"
 | 
			
		||||
 | 
			
		||||
<PurchaseSteps PurchaseStep="PurchaseStep.SuccessfulPurchase" />
 | 
			
		||||
 | 
			
		||||
<section class="section">
 | 
			
		||||
	<div class="container">
 | 
			
		||||
 | 
			
		||||
		<div class="columns">
 | 
			
		||||
 | 
			
		||||
			<div class="column">
 | 
			
		||||
				<div class="box">
 | 
			
		||||
					<h1 class="title has-text-centered">
 | 
			
		||||
						<span>🎉</span>
 | 
			
		||||
						<span>Purchase completed with success!</span>
 | 
			
		||||
						<span>🎉</span>
 | 
			
		||||
					</h1>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
 | 
			
		||||
		</div>
 | 
			
		||||
 | 
			
		||||
	</div>
 | 
			
		||||
</section>
 | 
			
		||||
							
								
								
									
										163
									
								
								drinkMe/Client/Pages/Index.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,163 @@
 | 
			
		||||
@page "/"
 | 
			
		||||
 | 
			
		||||
<PurchaseSteps PurchaseStep="PurchaseStep.ChoosingProducts" />
 | 
			
		||||
 | 
			
		||||
<section class="section">
 | 
			
		||||
	<div class="container">
 | 
			
		||||
 | 
			
		||||
		<div class="columns">
 | 
			
		||||
 | 
			
		||||
			<div class="column is-half">
 | 
			
		||||
				<div class="columns is-multiline">
 | 
			
		||||
 | 
			
		||||
					@foreach (var drink in AvailableDrinks)
 | 
			
		||||
					{
 | 
			
		||||
						<div @key="drink.Id" class="column is-full">
 | 
			
		||||
							<div class="box">
 | 
			
		||||
								<article class="media">
 | 
			
		||||
									<div class="media-left">
 | 
			
		||||
										<figure class="image is-64x64">
 | 
			
		||||
											<img src="images/drinks/@drink.PictureName" alt="drink image">
 | 
			
		||||
										</figure>
 | 
			
		||||
									</div>
 | 
			
		||||
									<div class="media-content">
 | 
			
		||||
										<div class="content">
 | 
			
		||||
											<p>
 | 
			
		||||
												<strong>@drink.Name</strong>
 | 
			
		||||
												<br>
 | 
			
		||||
												<span>@drink.Description</span>
 | 
			
		||||
											</p>
 | 
			
		||||
										</div>
 | 
			
		||||
										<div class="level">
 | 
			
		||||
											<div class="level-left">
 | 
			
		||||
												<div class="level-item">
 | 
			
		||||
													<strong>@drink.Price €</strong>
 | 
			
		||||
												</div>
 | 
			
		||||
											</div>
 | 
			
		||||
											<div class="level-right mt-2-mobile">
 | 
			
		||||
												<div class="level-item has-text-centered">
 | 
			
		||||
													<button class="button is-small is-primary has-icons-left"
 | 
			
		||||
																	disabled="@drink.IsInTheCart" @onclick="async () => await AddToCart(drink)">
 | 
			
		||||
														<span class="icon"><i class="fad fa-cart-arrow-down"></i></span>
 | 
			
		||||
														<span>Add to cart</span>
 | 
			
		||||
													</button>
 | 
			
		||||
												</div>
 | 
			
		||||
											</div>
 | 
			
		||||
										</div>
 | 
			
		||||
									</div>
 | 
			
		||||
								</article>
 | 
			
		||||
							</div>
 | 
			
		||||
						</div>
 | 
			
		||||
					}
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
 | 
			
		||||
			<div class="column is-half">
 | 
			
		||||
 | 
			
		||||
				<div class="box">
 | 
			
		||||
					@if (CartDrinks.Count == 0)
 | 
			
		||||
					{
 | 
			
		||||
						<h1 class="has-text-centered">
 | 
			
		||||
							<span class="icon-text">
 | 
			
		||||
								<span class="icon">
 | 
			
		||||
									<i class="fad fa-shopping-cart"></i>
 | 
			
		||||
								</span>
 | 
			
		||||
								<span>Empty cart</span>
 | 
			
		||||
							</span>
 | 
			
		||||
						</h1>
 | 
			
		||||
					}
 | 
			
		||||
					else
 | 
			
		||||
					{
 | 
			
		||||
						<div class="drinkCartList">
 | 
			
		||||
							@foreach (var cartDrink in CartDrinks)
 | 
			
		||||
							{
 | 
			
		||||
								<div class="drinkCartItem">
 | 
			
		||||
 | 
			
		||||
									<div class="drinkCartName"><strong>@cartDrink.Name</strong></div>
 | 
			
		||||
									<div class="drinkCartQuantity">
 | 
			
		||||
										<div class="field has-addons">
 | 
			
		||||
											<p class="control">
 | 
			
		||||
												<button class="button is-small is-danger" @onclick="async () => await SubtractQuantity(cartDrink)">
 | 
			
		||||
													<span class="icon"><i class="fad fa-minus"></i></span>
 | 
			
		||||
												</button>
 | 
			
		||||
											</p>
 | 
			
		||||
											<p class="control">
 | 
			
		||||
												<input class="input is-small has-text-centered" type="text" readonly @bind-value="cartDrink.Quantity" />
 | 
			
		||||
											</p>
 | 
			
		||||
											<p class="control">
 | 
			
		||||
												<button class="button is-small is-success" @onclick="async () => await AddQuantity(cartDrink)">
 | 
			
		||||
													<span class="icon"><i class="fad fa-plus"></i></span>
 | 
			
		||||
												</button>
 | 
			
		||||
											</p>
 | 
			
		||||
										</div>
 | 
			
		||||
									</div>
 | 
			
		||||
									<div class="drinkCartCross has-text-centered">
 | 
			
		||||
										<span class="icon">
 | 
			
		||||
											<i class="fad fa-times"></i>
 | 
			
		||||
										</span>
 | 
			
		||||
									</div>
 | 
			
		||||
									<div class="drinkCartPrice has-text-centered">@cartDrink.Price €</div>
 | 
			
		||||
 | 
			
		||||
								</div>
 | 
			
		||||
							}
 | 
			
		||||
						</div>
 | 
			
		||||
						<hr />
 | 
			
		||||
						<EditForm Model="DiscountForm" OnValidSubmit="TryAddDiscount">
 | 
			
		||||
							<DataAnnotationsValidator />
 | 
			
		||||
							<label class="label" for="discount">
 | 
			
		||||
								<span>Any discount?</span>
 | 
			
		||||
							</label>
 | 
			
		||||
							<div class="field has-addons">
 | 
			
		||||
								<p class="control is-expanded has-icons-left">
 | 
			
		||||
									<span class="icon is-left"><i class="fad fa-tags"></i></span>
 | 
			
		||||
									<input class="input" type="text" id="discount" placeholder="discount code" @onchange="OnChangeDiscountCode">
 | 
			
		||||
								</p>
 | 
			
		||||
								<p class="control">
 | 
			
		||||
									<button type="submit" class="button is-primary has-icons-left">
 | 
			
		||||
										<span class="icon is-left">
 | 
			
		||||
											<i class="fad fa-badge-dollar"></i>
 | 
			
		||||
										</span>
 | 
			
		||||
										<span>Add discount</span>
 | 
			
		||||
									</button>
 | 
			
		||||
								</p>
 | 
			
		||||
							</div>
 | 
			
		||||
							<p class="help is-danger">
 | 
			
		||||
								<ValidationMessage For="() => DiscountForm.DiscountCode" />
 | 
			
		||||
								@InvalidDiscountCodeError
 | 
			
		||||
							</p>
 | 
			
		||||
							<ul>
 | 
			
		||||
								@foreach (var discountCode in DiscountCodes)
 | 
			
		||||
								{
 | 
			
		||||
									<li class="has-text-right">
 | 
			
		||||
										<span class="tag is-dark">@discountCode.Code</span>
 | 
			
		||||
										<span class="tag is-success">-@discountCode.DiscountPercentage %</span>
 | 
			
		||||
									</li>
 | 
			
		||||
								}
 | 
			
		||||
							</ul>
 | 
			
		||||
						</EditForm>
 | 
			
		||||
						<hr />
 | 
			
		||||
						<div class="level">
 | 
			
		||||
							<div class="level-left">
 | 
			
		||||
								<h2><strong>Totale price</strong></h2>
 | 
			
		||||
							</div>
 | 
			
		||||
							<div class="level-right">
 | 
			
		||||
								<h2>
 | 
			
		||||
									<strong>@(Math.Round(TotalPrice, 2)) €</strong>
 | 
			
		||||
								</h2>
 | 
			
		||||
							</div>
 | 
			
		||||
						</div>
 | 
			
		||||
						<hr />
 | 
			
		||||
						<div class="buttons is-right">
 | 
			
		||||
							<NavLink href="payment" class="button is-primary has-icons-left">
 | 
			
		||||
								<span class="icon"><i class="fad fa-money-bill-wave"></i></span>
 | 
			
		||||
								<span>Go to payment</span>
 | 
			
		||||
							</NavLink>
 | 
			
		||||
						</div>
 | 
			
		||||
					}
 | 
			
		||||
				</div>
 | 
			
		||||
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										106
									
								
								drinkMe/Client/Pages/Index.razor.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,106 @@
 | 
			
		||||
using Blazored.LocalStorage;
 | 
			
		||||
using drinkMe.Client.Models;
 | 
			
		||||
using drinkMe.Client.Services.Interfaces;
 | 
			
		||||
using drinkMe.Shared;
 | 
			
		||||
using Microsoft.AspNetCore.Components;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Client.Pages
 | 
			
		||||
{
 | 
			
		||||
	public partial class Index : ComponentBase
 | 
			
		||||
	{
 | 
			
		||||
		[Inject] ILocalStorageService LocalStorage { get; set; }
 | 
			
		||||
		[Inject] IPriceService PriceService { get; set; }
 | 
			
		||||
		[Inject] IDataService DataService { get; set; }
 | 
			
		||||
		protected List<DrinkViewModel> AvailableDrinks { get; set; } = new List<DrinkViewModel>();
 | 
			
		||||
		protected List<DiscountCodeViewModel> DiscountCodes { get; set; } = new List<DiscountCodeViewModel>();
 | 
			
		||||
		protected List<DrinkViewModel> CartDrinks { get; set; } = new List<DrinkViewModel>();
 | 
			
		||||
		protected string GetDrinksError { get; set; }
 | 
			
		||||
		protected string InvalidDiscountCodeError { get; set; }
 | 
			
		||||
		protected DiscountForm DiscountForm { get; set; } = new DiscountForm();
 | 
			
		||||
		protected float TotalPrice { get; set; }
 | 
			
		||||
 | 
			
		||||
		protected override async Task OnInitializedAsync()
 | 
			
		||||
		{
 | 
			
		||||
			var getDrinksResult = await DataService.GetDrinks();
 | 
			
		||||
			if (!getDrinksResult.IsValid)
 | 
			
		||||
			{
 | 
			
		||||
				GetDrinksError = getDrinksResult.ErrorMessage;
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			AvailableDrinks = (List<DrinkViewModel>)getDrinksResult.Data;
 | 
			
		||||
			DiscountCodes = await LocalStorage.GetItemAsync<List<DiscountCodeViewModel>>(nameof(DiscountCodes)) ?? new List<DiscountCodeViewModel>();
 | 
			
		||||
			CartDrinks = await LocalStorage.GetItemAsync<List<DrinkViewModel>>(nameof(CartDrinks)) ?? new List<DrinkViewModel>();
 | 
			
		||||
			if (CartDrinks.Count > 0)
 | 
			
		||||
			{
 | 
			
		||||
				TotalPrice = PriceService.GetTotalPrice(DiscountCodes, CartDrinks);
 | 
			
		||||
				AvailableDrinks.ForEach(ad => ad.IsInTheCart = CartDrinks.Any(cd => cd.Id == ad.Id));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		protected async Task AddQuantity(DrinkViewModel drink)
 | 
			
		||||
		{
 | 
			
		||||
			drink.Quantity = drink.Quantity is > 0 and < 20 ? ++drink.Quantity : drink.Quantity;
 | 
			
		||||
			await LocalStorage.SetItemAsync(nameof(CartDrinks), CartDrinks);
 | 
			
		||||
			TotalPrice = PriceService.GetTotalPrice(DiscountCodes, CartDrinks);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		protected async Task SubtractQuantity(DrinkViewModel drink)
 | 
			
		||||
		{
 | 
			
		||||
			if (drink.Quantity - 1 == 0)
 | 
			
		||||
			{
 | 
			
		||||
				var availableDrink = AvailableDrinks.First(ad => ad.Id == drink.Id);
 | 
			
		||||
				availableDrink.IsInTheCart = false;
 | 
			
		||||
				CartDrinks.Remove(drink);
 | 
			
		||||
				await LocalStorage.SetItemAsync(nameof(CartDrinks), CartDrinks);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			drink.Quantity = drink.Quantity is > 1 and < 21 ? --drink.Quantity : drink.Quantity;
 | 
			
		||||
			await LocalStorage.SetItemAsync(nameof(CartDrinks), CartDrinks);
 | 
			
		||||
			TotalPrice = PriceService.GetTotalPrice(DiscountCodes, CartDrinks);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		protected async Task AddToCart(DrinkViewModel drink)
 | 
			
		||||
		{
 | 
			
		||||
			drink.IsInTheCart = true;
 | 
			
		||||
			CartDrinks.Add(drink);
 | 
			
		||||
			await LocalStorage.SetItemAsync(nameof(CartDrinks), CartDrinks);
 | 
			
		||||
			TotalPrice = PriceService.GetTotalPrice(DiscountCodes, CartDrinks);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		protected async Task TryAddDiscount()
 | 
			
		||||
		{
 | 
			
		||||
			InvalidDiscountCodeError = default;
 | 
			
		||||
 | 
			
		||||
			if (DiscountCodes.Any(dc => dc.Code == DiscountForm.DiscountCode))
 | 
			
		||||
			{
 | 
			
		||||
				InvalidDiscountCodeError = "Discount code already added.";
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var validationResult = await DataService.IsValidDiscountCode(DiscountForm.DiscountCode);
 | 
			
		||||
			if (!validationResult.IsValid)
 | 
			
		||||
			{
 | 
			
		||||
				InvalidDiscountCodeError = validationResult.ErrorMessage;
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var discount = validationResult.Data as DiscountCodeViewModel;
 | 
			
		||||
 | 
			
		||||
			DiscountCodes.Add(discount);
 | 
			
		||||
			await LocalStorage.SetItemAsync(nameof(DiscountCodes), DiscountCodes);
 | 
			
		||||
			TotalPrice = PriceService.GetTotalPrice(DiscountCodes, CartDrinks);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		protected void OnChangeDiscountCode(ChangeEventArgs changeEvent)
 | 
			
		||||
		{
 | 
			
		||||
			InvalidDiscountCodeError = default;
 | 
			
		||||
			DiscountForm.DiscountCode = (string)changeEvent.Value;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										172
									
								
								drinkMe/Client/Pages/Payment.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,172 @@
 | 
			
		||||
@page "/payment"
 | 
			
		||||
 | 
			
		||||
<PurchaseSteps PurchaseStep="PurchaseStep.PaymentSetup" />
 | 
			
		||||
 | 
			
		||||
<section class="section">
 | 
			
		||||
	<div class="container">
 | 
			
		||||
 | 
			
		||||
		<div class="columns">
 | 
			
		||||
 | 
			
		||||
			<div class="column is-half">
 | 
			
		||||
 | 
			
		||||
				<div class="box">
 | 
			
		||||
					@if (CartDrinks.Count == 0)
 | 
			
		||||
					{
 | 
			
		||||
						<h1 class="has-text-centered">
 | 
			
		||||
							<span class="icon-text">
 | 
			
		||||
								<span class="icon">
 | 
			
		||||
									<i class="fad fa-shopping-cart"></i>
 | 
			
		||||
								</span>
 | 
			
		||||
								<span>Empty cart</span>
 | 
			
		||||
							</span>
 | 
			
		||||
						</h1>
 | 
			
		||||
					}
 | 
			
		||||
					else
 | 
			
		||||
					{
 | 
			
		||||
						<div class="drinkCartList">
 | 
			
		||||
							@foreach (var cartDrink in CartDrinks)
 | 
			
		||||
							{
 | 
			
		||||
								<div class="drinkCartPItem">
 | 
			
		||||
 | 
			
		||||
									<div class="drinkCartName"><strong>@cartDrink.Name</strong></div>
 | 
			
		||||
									<div class="drinkCartQuantity has-text-centered">
 | 
			
		||||
										<div class="field">
 | 
			
		||||
											<p class="control">
 | 
			
		||||
												@cartDrink.Quantity
 | 
			
		||||
											</p>
 | 
			
		||||
										</div>
 | 
			
		||||
									</div>
 | 
			
		||||
									<div class="drinkCartCross has-text-centered">
 | 
			
		||||
										<span class="icon">
 | 
			
		||||
											<i class="fad fa-times"></i>
 | 
			
		||||
										</span>
 | 
			
		||||
									</div>
 | 
			
		||||
									<div class="drinkCartPrice has-text-centered">@cartDrink.Price €</div>
 | 
			
		||||
 | 
			
		||||
								</div>
 | 
			
		||||
							}
 | 
			
		||||
						</div>
 | 
			
		||||
						if (DiscountCodes.Count > 0)
 | 
			
		||||
						{
 | 
			
		||||
							<hr />
 | 
			
		||||
							<ul>
 | 
			
		||||
								@foreach (var discountCode in DiscountCodes)
 | 
			
		||||
								{
 | 
			
		||||
									<li class="has-text-right">
 | 
			
		||||
										<span class="tag is-dark">@discountCode.Code</span>
 | 
			
		||||
										<span class="tag is-success">-@discountCode.DiscountPercentage %</span>
 | 
			
		||||
									</li>
 | 
			
		||||
								}
 | 
			
		||||
							</ul>
 | 
			
		||||
						}
 | 
			
		||||
						<hr />
 | 
			
		||||
						<div class="level">
 | 
			
		||||
							<div class="level-left">
 | 
			
		||||
								<h2><strong>Totale price</strong></h2>
 | 
			
		||||
							</div>
 | 
			
		||||
							<div class="level-right">
 | 
			
		||||
								<h2>
 | 
			
		||||
									<strong>@(Math.Round(TotalPrice, 2)) €</strong>
 | 
			
		||||
								</h2>
 | 
			
		||||
							</div>
 | 
			
		||||
						</div>
 | 
			
		||||
					}
 | 
			
		||||
				</div>
 | 
			
		||||
 | 
			
		||||
			</div>
 | 
			
		||||
 | 
			
		||||
			<div class="column is-half">
 | 
			
		||||
				<div class="box">
 | 
			
		||||
					<EditForm Model="CreditCardForm" OnValidSubmit="Pay">
 | 
			
		||||
						<DataAnnotationsValidator />
 | 
			
		||||
						<InputRadioGroup Name="PaymentMethod" @bind-Value="PaymentMethod">
 | 
			
		||||
							<div class="field">
 | 
			
		||||
								<div class="control">
 | 
			
		||||
									<label class="radio">
 | 
			
		||||
										<InputRadio Name="PaymentMethod" Value="PaymentMethod.Cash" disabled="@(TotalPrice > 10f)" />
 | 
			
		||||
										<span class="icon-text"><span class="icon"><i class="fad fa-money-bill-wave"></i></span> <span>I'm going with cash</span></span>
 | 
			
		||||
									</label>
 | 
			
		||||
								</div>
 | 
			
		||||
							</div>
 | 
			
		||||
							<div class="is-divider" data-content="OR"></div>
 | 
			
		||||
							<div class="field">
 | 
			
		||||
								<div class="control">
 | 
			
		||||
									<label class="radio">
 | 
			
		||||
										<InputRadio Name="PaymentMethod" Value="PaymentMethod.CreditCard" />
 | 
			
		||||
										<span class="icon-text"><span class="icon"><i class="fad fa-credit-card-front"></i></span> <span>Here goes the card</span></span>
 | 
			
		||||
									</label>
 | 
			
		||||
								</div>
 | 
			
		||||
							</div>
 | 
			
		||||
						</InputRadioGroup>
 | 
			
		||||
						<fieldset class="@VUtilities.IfTrueThen(PaymentMethod == PaymentMethod.Cash, "is-hidden")" disabled="@(PaymentMethod != PaymentMethod.CreditCard)">
 | 
			
		||||
							<div class="field">
 | 
			
		||||
								<label class="label">Credit card number</label>
 | 
			
		||||
								<div class="control has-icons-left">
 | 
			
		||||
									<span class="icon is-left"><i class="fad fa-credit-card-front"></i></span>
 | 
			
		||||
									<input @bind-value="CreditCardForm.Number" class="input" placeholder="">
 | 
			
		||||
								</div>
 | 
			
		||||
								<p class="help is-danger">
 | 
			
		||||
									<ValidationMessage For="() => CreditCardForm.Number" />
 | 
			
		||||
								</p>
 | 
			
		||||
							</div>
 | 
			
		||||
							<div class="columns">
 | 
			
		||||
								<div class="column is-half">
 | 
			
		||||
									<label class="label">Expiration month / year</label>
 | 
			
		||||
									<div class="field has-addons has-addons-right">
 | 
			
		||||
										<div class="control">
 | 
			
		||||
											<input @bind-value="CreditCardForm.ExpirationMonth" class="input has-text-centered" placeholder="01">
 | 
			
		||||
										</div>
 | 
			
		||||
										<div class="control">
 | 
			
		||||
											<input class="input has-text-centered" value="/" readonly disabled>
 | 
			
		||||
										</div>
 | 
			
		||||
										<div class="control">
 | 
			
		||||
											<input @bind-value="CreditCardForm.ExpirationYear" class="input has-text-centered" placeholder="@((DateTime.Now.Year + 1).ToString().Substring(2,2))">
 | 
			
		||||
										</div>
 | 
			
		||||
									</div>
 | 
			
		||||
									<p class="help is-danger">
 | 
			
		||||
										<ValidationMessage For="() => CreditCardForm.ExpirationMonth" />
 | 
			
		||||
										<ValidationMessage For="() => CreditCardForm.ExpirationYear" />
 | 
			
		||||
									</p>
 | 
			
		||||
								</div>
 | 
			
		||||
								<div class="column is-half">
 | 
			
		||||
									<div class="field">
 | 
			
		||||
										<label class="label">CVV code</label>
 | 
			
		||||
										<div class="control has-icons-left">
 | 
			
		||||
											<span class="icon is-left"><i class="fad fa-key"></i></span>
 | 
			
		||||
											<input type="text" @bind-value="CreditCardForm.CVVCode" class="input has-text-centered" placeholder="***" />
 | 
			
		||||
										</div>
 | 
			
		||||
										<p class="help is-danger">
 | 
			
		||||
											<ValidationMessage For="() => CreditCardForm.CVVCode" />
 | 
			
		||||
										</p>
 | 
			
		||||
									</div>
 | 
			
		||||
								</div>
 | 
			
		||||
							</div>
 | 
			
		||||
							@if (PaymentMethod == PaymentMethod.CreditCard)
 | 
			
		||||
							{
 | 
			
		||||
								<div class="field has-text-right">
 | 
			
		||||
									<button type="submit" class="button is-primary has-icons-left">
 | 
			
		||||
										<span class="icon"><i class="fad fa-credit-card-blank"></i></span>
 | 
			
		||||
										<span>Pay</span>
 | 
			
		||||
									</button>
 | 
			
		||||
								</div>
 | 
			
		||||
							}
 | 
			
		||||
						</fieldset>
 | 
			
		||||
 | 
			
		||||
					</EditForm>
 | 
			
		||||
 | 
			
		||||
					@if (PaymentMethod == PaymentMethod.Cash)
 | 
			
		||||
					{
 | 
			
		||||
						<div class="field mt-5 has-text-right">
 | 
			
		||||
							<button class="button is-primary has-icons-left" @onclick="Pay">
 | 
			
		||||
								<span class="icon"><i class="fad fa-money-bill-wave"></i></span>
 | 
			
		||||
								<span>Pay at checkout</span>
 | 
			
		||||
							</button>
 | 
			
		||||
						</div>
 | 
			
		||||
					}
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										69
									
								
								drinkMe/Client/Pages/Payment.razor.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,69 @@
 | 
			
		||||
using Blazored.LocalStorage;
 | 
			
		||||
using drinkMe.Client.Models;
 | 
			
		||||
using drinkMe.Client.Services.Interfaces;
 | 
			
		||||
using drinkMe.Shared;
 | 
			
		||||
using Microsoft.AspNetCore.Components;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Client.Pages
 | 
			
		||||
{
 | 
			
		||||
	public partial class Payment : ComponentBase
 | 
			
		||||
	{
 | 
			
		||||
		[Inject] ILocalStorageService LocalStorage { get; set; }
 | 
			
		||||
		[Inject] IPriceService PriceService { get; set; }
 | 
			
		||||
		[Inject] IPaymentService PaymentService { get; set; }
 | 
			
		||||
		[Inject] NavigationManager Navigation { get; set; }
 | 
			
		||||
		protected List<DrinkViewModel> CartDrinks { get; set; } = new List<DrinkViewModel>();
 | 
			
		||||
		protected List<DiscountCodeViewModel> DiscountCodes { get; set; } = new List<DiscountCodeViewModel>();
 | 
			
		||||
		protected CreditCardForm CreditCardForm { get; set; } = new CreditCardForm();
 | 
			
		||||
		protected PaymentMethod PaymentMethod { get; set; } = PaymentMethod.Undefined;
 | 
			
		||||
		protected float TotalPrice { get; set; }
 | 
			
		||||
		protected string PaymentError { get; set; }
 | 
			
		||||
 | 
			
		||||
		protected override async Task OnInitializedAsync()
 | 
			
		||||
		{
 | 
			
		||||
			CartDrinks = await LocalStorage.GetItemAsync<List<DrinkViewModel>>(nameof(CartDrinks));
 | 
			
		||||
			DiscountCodes = await LocalStorage.GetItemAsync<List<DiscountCodeViewModel>>(nameof(DiscountCodes)) ?? new List<DiscountCodeViewModel>();
 | 
			
		||||
 | 
			
		||||
			TotalPrice = PriceService.GetTotalPrice(DiscountCodes, CartDrinks);
 | 
			
		||||
			if (TotalPrice > 10f)
 | 
			
		||||
				PaymentMethod = PaymentMethod.CreditCard;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		protected async Task Pay()
 | 
			
		||||
		{
 | 
			
		||||
			PaymentError = default;
 | 
			
		||||
			var paymentCart = new PurchaseCart();
 | 
			
		||||
			paymentCart.Discounts = DiscountCodes;
 | 
			
		||||
			paymentCart.PurchasingItems = CartDrinks.Select(cd => new CartItem
 | 
			
		||||
			{
 | 
			
		||||
				Id = cd.Id,
 | 
			
		||||
				Quantity = cd.Quantity
 | 
			
		||||
			}).ToList();
 | 
			
		||||
			paymentCart.IsPayedWithCash = PaymentMethod == PaymentMethod.Cash;
 | 
			
		||||
			if (!paymentCart.IsPayedWithCash)
 | 
			
		||||
			{
 | 
			
		||||
				paymentCart.CreditCardNumber = CreditCardForm.Number;
 | 
			
		||||
				paymentCart.CreditCardExpirationMonth = CreditCardForm.ExpirationMonth;
 | 
			
		||||
				paymentCart.CreditCardExpirationYear = CreditCardForm.ExpirationYear;
 | 
			
		||||
				paymentCart.CreditCardCVVCode = CreditCardForm.CVVCode;
 | 
			
		||||
			}
 | 
			
		||||
			var paymentResult = await PaymentService.Pay(paymentCart);
 | 
			
		||||
 | 
			
		||||
			if (!paymentResult.IsValid)
 | 
			
		||||
			{
 | 
			
		||||
				PaymentError = paymentResult.ErrorMessage;
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			await LocalStorage.RemoveItemAsync(nameof(CartDrinks));
 | 
			
		||||
			await LocalStorage.RemoveItemAsync(nameof(DiscountCodes));
 | 
			
		||||
 | 
			
		||||
			Navigation.NavigateTo("done");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								drinkMe/Client/Program.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,39 @@
 | 
			
		||||
using drinkMe.Client.Services;
 | 
			
		||||
using drinkMe.Client.Services.Interfaces;
 | 
			
		||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
 | 
			
		||||
using Microsoft.Extensions.Configuration;
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Net.Http;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Blazored.LocalStorage;
 | 
			
		||||
using System.Text.Json;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Client
 | 
			
		||||
{
 | 
			
		||||
	public class Program
 | 
			
		||||
	{
 | 
			
		||||
		public static async Task Main(string[] args)
 | 
			
		||||
		{
 | 
			
		||||
			var builder = WebAssemblyHostBuilder.CreateDefault(args);
 | 
			
		||||
			builder.RootComponents.Add<App>("#app");
 | 
			
		||||
 | 
			
		||||
			builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) })
 | 
			
		||||
				.AddScoped<IDataService, DataService>()
 | 
			
		||||
				.AddScoped<IPriceService, PriceService>()
 | 
			
		||||
				.AddScoped<IPaymentService, PaymentService>();
 | 
			
		||||
 | 
			
		||||
			builder.Services.AddBlazoredLocalStorage(config =>
 | 
			
		||||
			{
 | 
			
		||||
				config.JsonSerializerOptions.WriteIndented = false;
 | 
			
		||||
				config.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
 | 
			
		||||
				config.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			await builder.Build().RunAsync();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								drinkMe/Client/Properties/launchSettings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,30 @@
 | 
			
		||||
{
 | 
			
		||||
  "iisSettings": {
 | 
			
		||||
    "windowsAuthentication": false,
 | 
			
		||||
    "anonymousAuthentication": true,
 | 
			
		||||
    "iisExpress": {
 | 
			
		||||
      "applicationUrl": "http://localhost:14962",
 | 
			
		||||
      "sslPort": 44321
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "profiles": {
 | 
			
		||||
    "IIS Express": {
 | 
			
		||||
      "commandName": "IISExpress",
 | 
			
		||||
      "launchBrowser": true,
 | 
			
		||||
      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
 | 
			
		||||
      "environmentVariables": {
 | 
			
		||||
        "ASPNETCORE_ENVIRONMENT": "Development"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "drinkMe": {
 | 
			
		||||
      "commandName": "Project",
 | 
			
		||||
      "dotnetRunMessages": "true",
 | 
			
		||||
      "launchBrowser": true,
 | 
			
		||||
      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
 | 
			
		||||
      "applicationUrl": "https://localhost:5001;http://localhost:5000",
 | 
			
		||||
      "environmentVariables": {
 | 
			
		||||
        "ASPNETCORE_ENVIRONMENT": "Development"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								drinkMe/Client/SCSS/all.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,6 @@
 | 
			
		||||
@import "variables.scss";
 | 
			
		||||
@import "mixins.scss";
 | 
			
		||||
@import "framework-override.scss";
 | 
			
		||||
@import "neomorph.scss";
 | 
			
		||||
@import "base.scss";
 | 
			
		||||
@import "chooseDrinks.scss";
 | 
			
		||||
							
								
								
									
										41
									
								
								drinkMe/Client/SCSS/base.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,41 @@
 | 
			
		||||
html {
 | 
			
		||||
	scrollbar-width: thin;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
*, ::after, ::before {
 | 
			
		||||
	scrollbar-width: inherit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
::-webkit-scrollbar {
 | 
			
		||||
	width: 8px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
::-webkit-scrollbar-button {
 | 
			
		||||
	width: 8px;
 | 
			
		||||
	height: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
::-webkit-scrollbar-track {
 | 
			
		||||
	background: transparent;
 | 
			
		||||
	border: thin solid transparent;
 | 
			
		||||
	box-shadow: none;
 | 
			
		||||
	border-radius: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
::-webkit-scrollbar-thumb {
 | 
			
		||||
	border: thin solid transparent;
 | 
			
		||||
	border-radius: 10px;
 | 
			
		||||
	background: var(--primary-color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
body {
 | 
			
		||||
	background-image: url("/images/background.jpg");
 | 
			
		||||
	background-size: cover;
 | 
			
		||||
	min-height: 100vh
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.loading {
 | 
			
		||||
	height: 100vh;
 | 
			
		||||
	justify-content: center;
 | 
			
		||||
	flex-direction: column;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										55
									
								
								drinkMe/Client/SCSS/chooseDrinks.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,55 @@
 | 
			
		||||
.drinkCart {
 | 
			
		||||
 | 
			
		||||
	&List {
 | 
			
		||||
		display: flex;
 | 
			
		||||
		flex-direction: column;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	&Item {
 | 
			
		||||
		display: grid;
 | 
			
		||||
		grid-template-areas: "drinkName drinkQuantity drinkCross drinkPrice";
 | 
			
		||||
		grid-template-columns: 1fr minmax(100px, 20%) 5% 10%;
 | 
			
		||||
		grid-template-rows: 100%;
 | 
			
		||||
		grid-gap: 5px;
 | 
			
		||||
		align-items: center;
 | 
			
		||||
 | 
			
		||||
		&:not(:last-child) {
 | 
			
		||||
			margin-bottom: 1rem;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	&PItem {
 | 
			
		||||
		display: grid;
 | 
			
		||||
		grid-template-areas: "drinkName drinkQuantity drinkCross drinkPrice";
 | 
			
		||||
		grid-template-columns: 1fr 10% 5% 10%;
 | 
			
		||||
		grid-template-rows: 100%;
 | 
			
		||||
		grid-gap: 5px;
 | 
			
		||||
		align-items: center;
 | 
			
		||||
 | 
			
		||||
		&:not(:last-child) {
 | 
			
		||||
			margin-bottom: 1rem;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	&Name {
 | 
			
		||||
		grid-area: drinkName;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	&Cross {
 | 
			
		||||
		grid-area: drinkCross;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	&Quantity {
 | 
			
		||||
		grid-area: drinkQuantity;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	&Price {
 | 
			
		||||
		grid-area: drinkPrice;
 | 
			
		||||
		white-space: nowrap;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.column>.box>.title{
 | 
			
		||||
	padding: 10%;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								drinkMe/Client/SCSS/framework-override.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,16 @@
 | 
			
		||||
html {
 | 
			
		||||
	overflow-y: auto;
 | 
			
		||||
}
 | 
			
		||||
.steps {
 | 
			
		||||
	list-style-type: none !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@include MediaQuery(phone) {
 | 
			
		||||
	.mt-2-mobile {
 | 
			
		||||
		margin-top: .5rem !important;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.control.has-icons-left .icon, .control.has-icons-right .icon {
 | 
			
		||||
	z-index: 5;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								drinkMe/Client/SCSS/mixins.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,47 @@
 | 
			
		||||
//MEDIA QUERY MANAGER
 | 
			
		||||
 | 
			
		||||
/*$breakpoint argument choices:
 | 
			
		||||
- phone
 | 
			
		||||
- tab-port
 | 
			
		||||
- tab-land
 | 
			
		||||
- desk
 | 
			
		||||
- big-desktop
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
@mixin MediaQuery($breakpoint) {
 | 
			
		||||
	@if $breakpoint==phone {
 | 
			
		||||
		@media screen and (max-width: 768px) {
 | 
			
		||||
			@content
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@if $breakpoint==tab-p {
 | 
			
		||||
		@media screen and (min-width: 768px) and (max-width: 900px) {
 | 
			
		||||
			@content
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@if $breakpoint==tab-l {
 | 
			
		||||
		@media screen and (min-width: 901px) and (max-width: 1200px) {
 | 
			
		||||
			@content
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@if $breakpoint==laptop {
 | 
			
		||||
		@media screen and (min-width: 1201px) and (max-width: 1366px) {
 | 
			
		||||
			@content
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@if $breakpoint==desk {
 | 
			
		||||
		@media screen and (min-width: 1201px) and (max-width: 1800px) {
 | 
			
		||||
			@content
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@if $breakpoint==big-d {
 | 
			
		||||
		@media screen and (min-width: 1801px) and (max-width: 4000px) {
 | 
			
		||||
			@content
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										155
									
								
								drinkMe/Client/SCSS/neomorph.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,155 @@
 | 
			
		||||
.neomorph {
 | 
			
		||||
	box-shadow: -8px -8px 16px var(--light-shadow), 8px 8px 16px var(--dark-shadow);
 | 
			
		||||
 | 
			
		||||
	&Small {
 | 
			
		||||
		box-shadow: -6px -6px 12px var(--light-shadow), 6px 6px 12px var(--dark-shadow);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	&XSmall {
 | 
			
		||||
		box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	&XXSmall {
 | 
			
		||||
		box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	&Bottom {
 | 
			
		||||
		filter: drop-shadow(8px 8px 14px var(--primary-color-dark));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	&Inset {
 | 
			
		||||
		box-shadow: inset 8px 8px 16px var(--dark-shadow), inset -8px -8px 16px var(--light-shadow);
 | 
			
		||||
 | 
			
		||||
		&Small {
 | 
			
		||||
			box-shadow: inset 6px 6px 12px var(--dark-shadow), inset -6px -6px 12px var(--light-shadow);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		&XSmall {
 | 
			
		||||
			box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		&XXSmall {
 | 
			
		||||
			box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.neo {
 | 
			
		||||
	&Btn {
 | 
			
		||||
		background: linear-gradient(145deg,var(--primary-gradiend-light),var(--primary-gradiend-dark));
 | 
			
		||||
		box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow);
 | 
			
		||||
		border: none !important;
 | 
			
		||||
		-webkit-backface-visibility: hidden;
 | 
			
		||||
		backface-visibility: hidden;
 | 
			
		||||
 | 
			
		||||
		&:focus {
 | 
			
		||||
			box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow) !important;
 | 
			
		||||
			border: none !important;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		&InsetPlain {
 | 
			
		||||
			background: linear-gradient(145deg,var(--primary-gradiend-dark),var(--primary-gradiend-light));
 | 
			
		||||
			box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
 | 
			
		||||
			border: none !important;
 | 
			
		||||
 | 
			
		||||
			&:focus {
 | 
			
		||||
				box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow) !important;
 | 
			
		||||
				border: none !important;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		&Small {
 | 
			
		||||
			background: linear-gradient(145deg,var(--primary-gradiend-light),var(--primary-gradiend-dark));
 | 
			
		||||
			box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow);
 | 
			
		||||
			border: none !important;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
			&:focus {
 | 
			
		||||
				box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow);
 | 
			
		||||
				border: none !important;
 | 
			
		||||
 | 
			
		||||
				&:not(:active) {
 | 
			
		||||
					box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			&InsetPlain {
 | 
			
		||||
				background: linear-gradient(145deg,var(--primary-gradiend-dark),var(--primary-gradiend-light));
 | 
			
		||||
				box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
 | 
			
		||||
				border: none !important;
 | 
			
		||||
 | 
			
		||||
				&:focus {
 | 
			
		||||
					box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow) !important;
 | 
			
		||||
					border: none !important;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			&XInsetPlain {
 | 
			
		||||
				background: linear-gradient(145deg,var(--primary-gradiend-dark),var(--primary-gradiend-light));
 | 
			
		||||
				box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
 | 
			
		||||
				border: none !important;
 | 
			
		||||
 | 
			
		||||
				&:focus {
 | 
			
		||||
					box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow) !important;
 | 
			
		||||
					border: none !important;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			&Plain {
 | 
			
		||||
				box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow);
 | 
			
		||||
				border: none !important;
 | 
			
		||||
 | 
			
		||||
				&:focus {
 | 
			
		||||
					box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow) !important;
 | 
			
		||||
					border: none !important;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	&File {
 | 
			
		||||
		box-shadow: 0px 0px 0px var(--light-shadow), 0px 0px 0px var(--dark-shadow) !important;
 | 
			
		||||
		transition: all .2s linear;
 | 
			
		||||
		-webkit-backface-visibility: hidden !important;
 | 
			
		||||
		backface-visibility: hidden !important;
 | 
			
		||||
 | 
			
		||||
		@include MediaQuery(phone) {
 | 
			
		||||
			box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow) !important;
 | 
			
		||||
			background: var(--primary-color) !important;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		&:hover {
 | 
			
		||||
			box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow) !important;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		&.isSelected {
 | 
			
		||||
			background: linear-gradient(145deg,var(--primary-gradiend-dark),var(--primary-gradiend-light));
 | 
			
		||||
			box-shadow: inset 3px 3px 6px var(--dark-shadow),inset -3px -3px 6px var(--light-shadow) !important;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		&.is-active, &.active {
 | 
			
		||||
			box-shadow: inset 3px 3px 6px var(--dark-shadow),inset -3px -3px 6px var(--light-shadow) !important;
 | 
			
		||||
			color: var(--black) !important;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	&Input {
 | 
			
		||||
		box-shadow: inset 2px 2px 4px var(--dark-shadow),inset -2px -2px 4px var(--light-shadow) !important;
 | 
			
		||||
		background: linear-gradient(145deg,var(--primary-gradiend-dark),var(--primary-gradiend-light));
 | 
			
		||||
		border: none !important;
 | 
			
		||||
 | 
			
		||||
		&:focus {
 | 
			
		||||
			border: none !important;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	&Select > select {
 | 
			
		||||
		box-shadow: inset 2px 2px 4px var(--dark-shadow),inset -2px -2px 4px var(--light-shadow) !important;
 | 
			
		||||
		background: linear-gradient(145deg,var(--primary-gradiend-dark),var(--primary-gradiend-light)) !important;
 | 
			
		||||
		border: none !important;
 | 
			
		||||
 | 
			
		||||
		&:focus {
 | 
			
		||||
			border: none !important;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										56
									
								
								drinkMe/Client/SCSS/variables.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,56 @@
 | 
			
		||||
$main-pc: hsl(25,84,88); //#fbe9d7;
 | 
			
		||||
$main-gradient-light: hsl(25,84,92);
 | 
			
		||||
$main-gradient-dark: hsl(25,84,84);
 | 
			
		||||
$main-light-pc: lighten($main-pc,15%);
 | 
			
		||||
$main-dark-pc: darken($main-pc,25%);
 | 
			
		||||
$main-sc: #f6d5f7;
 | 
			
		||||
$main-light-sc: lighten($main-sc,15%);
 | 
			
		||||
$main-dark-sc: darken($main-sc,15%);
 | 
			
		||||
 | 
			
		||||
$icon-primary: hsl(0,96,25);
 | 
			
		||||
$icon-secondary: hsl(0,100,55);
 | 
			
		||||
 | 
			
		||||
$text-color: darken($main-pc,70%);
 | 
			
		||||
 | 
			
		||||
$black: hsl(0, 0%, 4%) !default;
 | 
			
		||||
$black-bis: hsl(0, 0%, 7%) !default;
 | 
			
		||||
$black-ter: hsl(0, 0%, 14%) !default;
 | 
			
		||||
 | 
			
		||||
$grey-darker: hsl(0, 0%, 21%) !default;
 | 
			
		||||
$grey-dark: hsl(0, 0%, 29%) !default;
 | 
			
		||||
$grey: hsl(0, 0%, 48%) !default;
 | 
			
		||||
$grey-light: hsl(0, 0%, 71%) !default;
 | 
			
		||||
$grey-lighter: hsl(0, 0%, 86%) !default;
 | 
			
		||||
 | 
			
		||||
$white-ter: hsl(0, 4%, 96%) !default;
 | 
			
		||||
$white-bis: hsl(0, 4%, 98%) !default;
 | 
			
		||||
$white: hsl(0, 4%, 99.8%) !default;
 | 
			
		||||
$danger: hsl(0, 82%, 91%);
 | 
			
		||||
$warning: hsl(48, 82%, 91%);
 | 
			
		||||
$info: hsl(196, 82%, 91%);
 | 
			
		||||
 | 
			
		||||
:root {
 | 
			
		||||
	--background: $main-pc;
 | 
			
		||||
	--text-color: $text-color;
 | 
			
		||||
	--black: $black;
 | 
			
		||||
	--black-bis: $black-bis;
 | 
			
		||||
	--black-ter: $black-ter;
 | 
			
		||||
	--white: $white;
 | 
			
		||||
	--primary-color: $main-pc;
 | 
			
		||||
	--primary-color-light: $main-light-pc;
 | 
			
		||||
	--primary-color-dark: $main-dark-pc;
 | 
			
		||||
	--primary-gradiend-light: $main-gradient-light;
 | 
			
		||||
	--primary-gradiend-dark: $main-gradient-dark;
 | 
			
		||||
	--secondary-color: $main-sc;
 | 
			
		||||
	--secondary-color-light: $main-light-sc;
 | 
			
		||||
	--secondary-color-dark: $main-dark-sc;
 | 
			
		||||
	--light-shadow: rgba($main-light-pc, .5);
 | 
			
		||||
	--dark-shadow: rgba($main-dark-pc, .5);
 | 
			
		||||
/*	--fa-primary-color: $text-color;
 | 
			
		||||
	--fa-secondary-color: $main-dark-pc;
 | 
			
		||||
	--fa-primary-opacity: 0.80;
 | 
			
		||||
	--fa-secondary-opacity: 0.80;*/
 | 
			
		||||
	--danger-color: $danger;
 | 
			
		||||
	--warning-color: $warning;
 | 
			
		||||
	--info-color: $info;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								drinkMe/Client/Services/DataService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,47 @@
 | 
			
		||||
using collAnon.Pub.Shared;
 | 
			
		||||
using drinkMe.Client.Services.Interfaces;
 | 
			
		||||
using drinkMe.Shared;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Net.Http;
 | 
			
		||||
using System.Text.Json;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Client.Services
 | 
			
		||||
{
 | 
			
		||||
	public class DataService : IDataService
 | 
			
		||||
	{
 | 
			
		||||
		readonly HttpClient HttpClient;
 | 
			
		||||
		public DataService(HttpClient httpClient)
 | 
			
		||||
		{
 | 
			
		||||
			HttpClient = httpClient;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public async Task<WebResult> IsValidDiscountCode(string code)
 | 
			
		||||
		{
 | 
			
		||||
			var result = new WebResult();
 | 
			
		||||
			var httpResponse = await HttpClient.GetAsync($"api/Drinks/{nameof(IsValidDiscountCode)}?{nameof(code)}={code}");
 | 
			
		||||
 | 
			
		||||
			if (!httpResponse.IsSuccessStatusCode)
 | 
			
		||||
				return result.Invalidate(JsonSerializer.Deserialize<WebResult>(await httpResponse.Content.ReadAsStringAsync(), new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }));
 | 
			
		||||
 | 
			
		||||
			result.Data = JsonSerializer.Deserialize<DiscountCodeViewModel>(await httpResponse.Content.ReadAsStringAsync(), new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
 | 
			
		||||
 | 
			
		||||
			return result;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public async Task<WebResult> GetDrinks()
 | 
			
		||||
		{
 | 
			
		||||
			var result = new WebResult();
 | 
			
		||||
			var httpResponse = await HttpClient.GetAsync($"api/Drinks/{nameof(GetDrinks)}");
 | 
			
		||||
 | 
			
		||||
			if (!httpResponse.IsSuccessStatusCode)
 | 
			
		||||
				return result.Invalidate(JsonSerializer.Deserialize<WebResult>(await httpResponse.Content.ReadAsStringAsync(), new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }));
 | 
			
		||||
 | 
			
		||||
			result.Data = JsonSerializer.Deserialize<List<DrinkViewModel>>(await httpResponse.Content.ReadAsStringAsync(), new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
 | 
			
		||||
 | 
			
		||||
			return result;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								drinkMe/Client/Services/Interfaces/IDataService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,11 @@
 | 
			
		||||
using collAnon.Pub.Shared;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Client.Services.Interfaces
 | 
			
		||||
{
 | 
			
		||||
	public interface IDataService
 | 
			
		||||
	{
 | 
			
		||||
		Task<WebResult> GetDrinks();
 | 
			
		||||
		Task<WebResult> IsValidDiscountCode(string code);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								drinkMe/Client/Services/Interfaces/IPaymentService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,14 @@
 | 
			
		||||
using collAnon.Pub.Shared;
 | 
			
		||||
using drinkMe.Shared;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Client.Services.Interfaces
 | 
			
		||||
{
 | 
			
		||||
	public interface IPaymentService
 | 
			
		||||
	{
 | 
			
		||||
		Task<WebResult> Pay(PurchaseCart purchaseCart);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								drinkMe/Client/Services/Interfaces/IPriceService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,13 @@
 | 
			
		||||
using drinkMe.Shared;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Client.Services.Interfaces
 | 
			
		||||
{
 | 
			
		||||
	public interface IPriceService
 | 
			
		||||
	{
 | 
			
		||||
		float GetTotalPrice(List<DiscountCodeViewModel> discountCodes, List<DrinkViewModel> drinks);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								drinkMe/Client/Services/PaymentService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,33 @@
 | 
			
		||||
using collAnon.Pub.Shared;
 | 
			
		||||
using drinkMe.Client.Services.Interfaces;
 | 
			
		||||
using drinkMe.Shared;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Net.Http;
 | 
			
		||||
using System.Net.Http.Json;
 | 
			
		||||
using System.Text.Json;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Client.Services
 | 
			
		||||
{
 | 
			
		||||
	public class PaymentService : IPaymentService
 | 
			
		||||
	{
 | 
			
		||||
		readonly HttpClient HttpClient;
 | 
			
		||||
		public PaymentService(HttpClient httpClient)
 | 
			
		||||
		{
 | 
			
		||||
			HttpClient = httpClient;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public async Task<WebResult> Pay(PurchaseCart purchaseCart)
 | 
			
		||||
		{
 | 
			
		||||
			var result = new WebResult();
 | 
			
		||||
			var httpResponse = await HttpClient.PostAsync($"api/Drinks/{nameof(Pay)}", JsonContent.Create(purchaseCart));
 | 
			
		||||
 | 
			
		||||
			if (!httpResponse.IsSuccessStatusCode)
 | 
			
		||||
				return result.Invalidate(JsonSerializer.Deserialize<WebResult>(await httpResponse.Content.ReadAsStringAsync(), new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }));
 | 
			
		||||
 | 
			
		||||
			return result;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										34
									
								
								drinkMe/Client/Services/PriceService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,34 @@
 | 
			
		||||
using Blazored.LocalStorage;
 | 
			
		||||
using drinkMe.Client.Services.Interfaces;
 | 
			
		||||
using drinkMe.Shared;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Client.Services
 | 
			
		||||
{
 | 
			
		||||
	public class PriceService : IPriceService
 | 
			
		||||
	{
 | 
			
		||||
		public float GetTotalPrice(List<DiscountCodeViewModel> discountCodes, List<DrinkViewModel> cartDrinks)
 | 
			
		||||
		{
 | 
			
		||||
			var total = default(float);
 | 
			
		||||
 | 
			
		||||
			if (discountCodes.Count == 0)
 | 
			
		||||
				foreach (var cartDrink in cartDrinks)
 | 
			
		||||
					total += cartDrink.Price * cartDrink.Quantity;
 | 
			
		||||
			else
 | 
			
		||||
				foreach (var cartDrink in cartDrinks)
 | 
			
		||||
					if (discountCodes.Any(d => d.ApplicableProducts.Contains(cartDrink.Id)))
 | 
			
		||||
					{
 | 
			
		||||
						var discountPercentage = discountCodes.First(d => d.ApplicableProducts.Contains(cartDrink.Id)).DiscountPercentage / 100;
 | 
			
		||||
						var discountedPricePortion = cartDrink.Price * discountPercentage;
 | 
			
		||||
						total += (cartDrink.Price - discountedPricePortion) * cartDrink.Quantity;
 | 
			
		||||
					}
 | 
			
		||||
					else
 | 
			
		||||
						total += cartDrink.Price * cartDrink.Quantity;
 | 
			
		||||
 | 
			
		||||
			return total;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								drinkMe/Client/Shared/MainLayout.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,10 @@
 | 
			
		||||
@inherits LayoutComponentBase
 | 
			
		||||
 | 
			
		||||
	<NavMenu />
 | 
			
		||||
 | 
			
		||||
	<section class="section">
 | 
			
		||||
		<div class="container">
 | 
			
		||||
			@Body
 | 
			
		||||
		</div>
 | 
			
		||||
	</section>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										37
									
								
								drinkMe/Client/Shared/NavMenu.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,37 @@
 | 
			
		||||
<nav class="navbar" role="navigation" aria-label="main navigation">
 | 
			
		||||
	<div class="navbar-brand">
 | 
			
		||||
		<NavLink class="navbar-item" href="/">
 | 
			
		||||
			<img src="/images/drinkMe-logo.png" alt="logo">
 | 
			
		||||
		</NavLink>
 | 
			
		||||
 | 
			
		||||
		<a role="button" class="navbar-burger @IsActive" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample"
 | 
			
		||||
			 @onclick="OpenCloseMenu">
 | 
			
		||||
			<span aria-hidden="true"></span>
 | 
			
		||||
			<span aria-hidden="true"></span>
 | 
			
		||||
			<span aria-hidden="true"></span>
 | 
			
		||||
		</a>
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<div id="navbarBasicExample" class="navbar-menu @IsActive">
 | 
			
		||||
		<div class="navbar-start">
 | 
			
		||||
			<NavLink class="navbar-item" href="/">
 | 
			
		||||
				<span class="icon-text">
 | 
			
		||||
					<span class="icon"><i class="fad fa-home"></i></span>
 | 
			
		||||
					<span>Home</span>
 | 
			
		||||
				</span>
 | 
			
		||||
			</NavLink>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
@code{
 | 
			
		||||
	string IsActive { get; set; }
 | 
			
		||||
 | 
			
		||||
	void OpenCloseMenu()
 | 
			
		||||
	{
 | 
			
		||||
		if (string.IsNullOrEmpty(IsActive))
 | 
			
		||||
			IsActive = "is-active";
 | 
			
		||||
		else
 | 
			
		||||
			IsActive = default;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								drinkMe/Client/_Imports.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,13 @@
 | 
			
		||||
@using System.Net.Http
 | 
			
		||||
@using System.Net.Http.Json
 | 
			
		||||
@using Microsoft.AspNetCore.Components.Forms
 | 
			
		||||
@using Microsoft.AspNetCore.Components.Routing
 | 
			
		||||
@using Microsoft.AspNetCore.Components.Web
 | 
			
		||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
 | 
			
		||||
@using Microsoft.AspNetCore.Components.WebAssembly.Http
 | 
			
		||||
@using Microsoft.JSInterop
 | 
			
		||||
@using drinkMe.Client
 | 
			
		||||
@using drinkMe.Client.Models
 | 
			
		||||
@using drinkMe.Client.Helpers
 | 
			
		||||
@using drinkMe.Client.Components
 | 
			
		||||
@using drinkMe.Client.Shared
 | 
			
		||||
							
								
								
									
										6
									
								
								drinkMe/Client/compilerconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,6 @@
 | 
			
		||||
[
 | 
			
		||||
  {
 | 
			
		||||
    "outputFile": "wwwroot/css/style.css",
 | 
			
		||||
    "inputFile": "SCSS/all.scss"
 | 
			
		||||
  }
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										63
									
								
								drinkMe/Client/compilerconfig.json.defaults
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,63 @@
 | 
			
		||||
{
 | 
			
		||||
  "compilers": {
 | 
			
		||||
    "less": {
 | 
			
		||||
      "autoPrefix": "",
 | 
			
		||||
      "cssComb": "none",
 | 
			
		||||
      "ieCompat": true,
 | 
			
		||||
      "strictMath": false,
 | 
			
		||||
      "strictUnits": false,
 | 
			
		||||
      "relativeUrls": true,
 | 
			
		||||
      "rootPath": "",
 | 
			
		||||
      "sourceMapRoot": "",
 | 
			
		||||
      "sourceMapBasePath": "",
 | 
			
		||||
      "sourceMap": false
 | 
			
		||||
    },
 | 
			
		||||
    "sass": {
 | 
			
		||||
      "autoPrefix": "",
 | 
			
		||||
      "includePath": "",
 | 
			
		||||
      "indentType": "space",
 | 
			
		||||
      "indentWidth": 2,
 | 
			
		||||
      "outputStyle": "nested",
 | 
			
		||||
      "Precision": 5,
 | 
			
		||||
      "relativeUrls": true,
 | 
			
		||||
      "sourceMapRoot": "",
 | 
			
		||||
      "lineFeed": "",
 | 
			
		||||
      "sourceMap": false
 | 
			
		||||
    },
 | 
			
		||||
    "stylus": {
 | 
			
		||||
      "sourceMap": false
 | 
			
		||||
    },
 | 
			
		||||
    "babel": {
 | 
			
		||||
      "sourceMap": false
 | 
			
		||||
    },
 | 
			
		||||
    "coffeescript": {
 | 
			
		||||
      "bare": false,
 | 
			
		||||
      "runtimeMode": "node",
 | 
			
		||||
      "sourceMap": false
 | 
			
		||||
    },
 | 
			
		||||
    "handlebars": {
 | 
			
		||||
      "root": "",
 | 
			
		||||
      "noBOM": false,
 | 
			
		||||
      "name": "",
 | 
			
		||||
      "namespace": "",
 | 
			
		||||
      "knownHelpersOnly": false,
 | 
			
		||||
      "forcePartial": false,
 | 
			
		||||
      "knownHelpers": [],
 | 
			
		||||
      "commonjs": "",
 | 
			
		||||
      "amd": false,
 | 
			
		||||
      "sourceMap": false
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "minifiers": {
 | 
			
		||||
    "css": {
 | 
			
		||||
      "enabled": true,
 | 
			
		||||
      "termSemicolons": true,
 | 
			
		||||
      "gzip": false
 | 
			
		||||
    },
 | 
			
		||||
    "javascript": {
 | 
			
		||||
      "enabled": true,
 | 
			
		||||
      "termSemicolons": true,
 | 
			
		||||
      "gzip": false
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								drinkMe/Client/drinkMe.Client.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,43 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <TargetFramework>net5.0</TargetFramework>
 | 
			
		||||
    <ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <Content Remove="compilerconfig.json" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <_ContentIncludedByDefault Remove="wwwroot\css\style.css" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <None Include="compilerconfig.json" />
 | 
			
		||||
    <None Include="wwwroot\webfonts\fa-duotone-900.svg" />
 | 
			
		||||
    <None Include="wwwroot\webfonts\fa-duotone-900.woff2" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <PackageReference Include="Blazored.LocalStorage" Version="3.0.0" />
 | 
			
		||||
    <PackageReference Include="Blazored.Modal" Version="6.0.1" />
 | 
			
		||||
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.5" />
 | 
			
		||||
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.5" PrivateAssets="all" />
 | 
			
		||||
    <PackageReference Include="System.Net.Http.Json" Version="5.0.0" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <ProjectReference Include="..\drinkMe.Client.Models\drinkMe.Client.Models.csproj" />
 | 
			
		||||
    <ProjectReference Include="..\Shared\drinkMe.Shared.csproj" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <Folder Include="wwwroot\images\drinks\" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
							
								
								
									
										1
									
								
								drinkMe/Client/wwwroot/css/bulma-divider.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1466
									
								
								drinkMe/Client/wwwroot/css/bulma-steps.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										3
									
								
								drinkMe/Client/wwwroot/css/bulma-steps.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										11705
									
								
								drinkMe/Client/wwwroot/css/bulma.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								drinkMe/Client/wwwroot/css/bulma.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										5605
									
								
								drinkMe/Client/wwwroot/css/duotone.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										5
									
								
								drinkMe/Client/wwwroot/css/duotone.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										7134
									
								
								drinkMe/Client/wwwroot/css/fontawesome.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										5
									
								
								drinkMe/Client/wwwroot/css/fontawesome.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										215
									
								
								drinkMe/Client/wwwroot/css/style.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,215 @@
 | 
			
		||||
:root {
 | 
			
		||||
  --background: #fadcc7;
 | 
			
		||||
  --text-color: #542707;
 | 
			
		||||
  --black: #0a0a0a;
 | 
			
		||||
  --black-bis: #121212;
 | 
			
		||||
  --black-ter: #242424;
 | 
			
		||||
  --white: #fffefe;
 | 
			
		||||
  --primary-color: #fadcc7;
 | 
			
		||||
  --primary-color-light: white;
 | 
			
		||||
  --primary-color-dark: #f09351;
 | 
			
		||||
  --primary-gradiend-light: #fce8d9;
 | 
			
		||||
  --primary-gradiend-dark: #f8d0b4;
 | 
			
		||||
  --secondary-color: #f6d5f7;
 | 
			
		||||
  --secondary-color-light: white;
 | 
			
		||||
  --secondary-color-dark: #e895eb;
 | 
			
		||||
  --light-shadow: rgba(255, 255, 255, 0.5);
 | 
			
		||||
  --dark-shadow: rgba(240, 147, 81, 0.5);
 | 
			
		||||
  /*	--fa-primary-color: $text-color;
 | 
			
		||||
	--fa-secondary-color: $main-dark-pc;
 | 
			
		||||
	--fa-primary-opacity: 0.80;
 | 
			
		||||
	--fa-secondary-opacity: 0.80;*/
 | 
			
		||||
  --danger-color: #fbd5d5;
 | 
			
		||||
  --warning-color: #fbf3d5;
 | 
			
		||||
  --info-color: #d5f1fb; }
 | 
			
		||||
 | 
			
		||||
/*$breakpoint argument choices:
 | 
			
		||||
- phone
 | 
			
		||||
- tab-port
 | 
			
		||||
- tab-land
 | 
			
		||||
- desk
 | 
			
		||||
- big-desktop
 | 
			
		||||
*/
 | 
			
		||||
html {
 | 
			
		||||
  overflow-y: auto; }
 | 
			
		||||
 | 
			
		||||
.steps {
 | 
			
		||||
  list-style-type: none !important; }
 | 
			
		||||
 | 
			
		||||
@media screen and (max-width: 768px) {
 | 
			
		||||
  .mt-2-mobile {
 | 
			
		||||
    margin-top: .5rem !important; } }
 | 
			
		||||
 | 
			
		||||
.control.has-icons-left .icon, .control.has-icons-right .icon {
 | 
			
		||||
  z-index: 5; }
 | 
			
		||||
 | 
			
		||||
.neomorph {
 | 
			
		||||
  box-shadow: -8px -8px 16px var(--light-shadow), 8px 8px 16px var(--dark-shadow); }
 | 
			
		||||
  .neomorphSmall {
 | 
			
		||||
    box-shadow: -6px -6px 12px var(--light-shadow), 6px 6px 12px var(--dark-shadow); }
 | 
			
		||||
  .neomorphXSmall {
 | 
			
		||||
    box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow); }
 | 
			
		||||
  .neomorphXXSmall {
 | 
			
		||||
    box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow); }
 | 
			
		||||
  .neomorphBottom {
 | 
			
		||||
    filter: drop-shadow(8px 8px 14px var(--primary-color-dark)); }
 | 
			
		||||
  .neomorphInset {
 | 
			
		||||
    box-shadow: inset 8px 8px 16px var(--dark-shadow), inset -8px -8px 16px var(--light-shadow); }
 | 
			
		||||
    .neomorphInsetSmall {
 | 
			
		||||
      box-shadow: inset 6px 6px 12px var(--dark-shadow), inset -6px -6px 12px var(--light-shadow); }
 | 
			
		||||
    .neomorphInsetXSmall {
 | 
			
		||||
      box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow); }
 | 
			
		||||
    .neomorphInsetXXSmall {
 | 
			
		||||
      box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow); }
 | 
			
		||||
 | 
			
		||||
.neoBtn {
 | 
			
		||||
  background: linear-gradient(145deg, var(--primary-gradiend-light), var(--primary-gradiend-dark));
 | 
			
		||||
  box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow);
 | 
			
		||||
  border: none !important;
 | 
			
		||||
  -webkit-backface-visibility: hidden;
 | 
			
		||||
  backface-visibility: hidden; }
 | 
			
		||||
  .neoBtn:focus {
 | 
			
		||||
    box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow) !important;
 | 
			
		||||
    border: none !important; }
 | 
			
		||||
  .neoBtnInsetPlain {
 | 
			
		||||
    background: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
 | 
			
		||||
    box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
 | 
			
		||||
    border: none !important; }
 | 
			
		||||
    .neoBtnInsetPlain:focus {
 | 
			
		||||
      box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow) !important;
 | 
			
		||||
      border: none !important; }
 | 
			
		||||
  .neoBtnSmall {
 | 
			
		||||
    background: linear-gradient(145deg, var(--primary-gradiend-light), var(--primary-gradiend-dark));
 | 
			
		||||
    box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow);
 | 
			
		||||
    border: none !important; }
 | 
			
		||||
    .neoBtnSmall:focus {
 | 
			
		||||
      box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow);
 | 
			
		||||
      border: none !important; }
 | 
			
		||||
      .neoBtnSmall:focus:not(:active) {
 | 
			
		||||
        box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow); }
 | 
			
		||||
    .neoBtnSmallInsetPlain {
 | 
			
		||||
      background: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
 | 
			
		||||
      box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
 | 
			
		||||
      border: none !important; }
 | 
			
		||||
      .neoBtnSmallInsetPlain:focus {
 | 
			
		||||
        box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow) !important;
 | 
			
		||||
        border: none !important; }
 | 
			
		||||
    .neoBtnSmallXInsetPlain {
 | 
			
		||||
      background: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
 | 
			
		||||
      box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
 | 
			
		||||
      border: none !important; }
 | 
			
		||||
      .neoBtnSmallXInsetPlain:focus {
 | 
			
		||||
        box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow) !important;
 | 
			
		||||
        border: none !important; }
 | 
			
		||||
    .neoBtnSmallPlain {
 | 
			
		||||
      box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow);
 | 
			
		||||
      border: none !important; }
 | 
			
		||||
      .neoBtnSmallPlain:focus {
 | 
			
		||||
        box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow) !important;
 | 
			
		||||
        border: none !important; }
 | 
			
		||||
 | 
			
		||||
.neoFile {
 | 
			
		||||
  box-shadow: 0px 0px 0px var(--light-shadow), 0px 0px 0px var(--dark-shadow) !important;
 | 
			
		||||
  transition: all .2s linear;
 | 
			
		||||
  -webkit-backface-visibility: hidden !important;
 | 
			
		||||
  backface-visibility: hidden !important; }
 | 
			
		||||
  @media screen and (max-width: 768px) {
 | 
			
		||||
    .neoFile {
 | 
			
		||||
      box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow) !important;
 | 
			
		||||
      background: var(--primary-color) !important; } }
 | 
			
		||||
  .neoFile:hover {
 | 
			
		||||
    box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow) !important; }
 | 
			
		||||
  .neoFile.isSelected {
 | 
			
		||||
    background: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
 | 
			
		||||
    box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow) !important; }
 | 
			
		||||
  .neoFile.is-active, .neoFile.active {
 | 
			
		||||
    box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow) !important;
 | 
			
		||||
    color: var(--black) !important; }
 | 
			
		||||
 | 
			
		||||
.neoInput {
 | 
			
		||||
  box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow) !important;
 | 
			
		||||
  background: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
 | 
			
		||||
  border: none !important; }
 | 
			
		||||
  .neoInput:focus {
 | 
			
		||||
    border: none !important; }
 | 
			
		||||
 | 
			
		||||
.neoSelect > select {
 | 
			
		||||
  box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow) !important;
 | 
			
		||||
  background: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light)) !important;
 | 
			
		||||
  border: none !important; }
 | 
			
		||||
  .neoSelect > select:focus {
 | 
			
		||||
    border: none !important; }
 | 
			
		||||
 | 
			
		||||
html {
 | 
			
		||||
  scrollbar-width: thin; }
 | 
			
		||||
 | 
			
		||||
*, ::after, ::before {
 | 
			
		||||
  scrollbar-width: inherit; }
 | 
			
		||||
 | 
			
		||||
::-webkit-scrollbar {
 | 
			
		||||
  width: 8px; }
 | 
			
		||||
 | 
			
		||||
::-webkit-scrollbar-button {
 | 
			
		||||
  width: 8px;
 | 
			
		||||
  height: 5px; }
 | 
			
		||||
 | 
			
		||||
::-webkit-scrollbar-track {
 | 
			
		||||
  background: transparent;
 | 
			
		||||
  border: thin solid transparent;
 | 
			
		||||
  box-shadow: none;
 | 
			
		||||
  border-radius: 10px; }
 | 
			
		||||
 | 
			
		||||
::-webkit-scrollbar-thumb {
 | 
			
		||||
  border: thin solid transparent;
 | 
			
		||||
  border-radius: 10px;
 | 
			
		||||
  background: var(--primary-color); }
 | 
			
		||||
 | 
			
		||||
body {
 | 
			
		||||
  background-image: url("/images/background.jpg");
 | 
			
		||||
  background-size: cover;
 | 
			
		||||
  min-height: 100vh; }
 | 
			
		||||
 | 
			
		||||
.loading {
 | 
			
		||||
  height: 100vh;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  flex-direction: column; }
 | 
			
		||||
 | 
			
		||||
.drinkCartList {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column; }
 | 
			
		||||
 | 
			
		||||
.drinkCartItem {
 | 
			
		||||
  display: grid;
 | 
			
		||||
  grid-template-areas: "drinkName drinkQuantity drinkCross drinkPrice";
 | 
			
		||||
  grid-template-columns: 1fr minmax(100px, 20%) 5% 10%;
 | 
			
		||||
  grid-template-rows: 100%;
 | 
			
		||||
  grid-gap: 5px;
 | 
			
		||||
  align-items: center; }
 | 
			
		||||
  .drinkCartItem:not(:last-child) {
 | 
			
		||||
    margin-bottom: 1rem; }
 | 
			
		||||
 | 
			
		||||
.drinkCartPItem {
 | 
			
		||||
  display: grid;
 | 
			
		||||
  grid-template-areas: "drinkName drinkQuantity drinkCross drinkPrice";
 | 
			
		||||
  grid-template-columns: 1fr 10% 5% 10%;
 | 
			
		||||
  grid-template-rows: 100%;
 | 
			
		||||
  grid-gap: 5px;
 | 
			
		||||
  align-items: center; }
 | 
			
		||||
  .drinkCartPItem:not(:last-child) {
 | 
			
		||||
    margin-bottom: 1rem; }
 | 
			
		||||
 | 
			
		||||
.drinkCartName {
 | 
			
		||||
  grid-area: drinkName; }
 | 
			
		||||
 | 
			
		||||
.drinkCartCross {
 | 
			
		||||
  grid-area: drinkCross; }
 | 
			
		||||
 | 
			
		||||
.drinkCartQuantity {
 | 
			
		||||
  grid-area: drinkQuantity; }
 | 
			
		||||
 | 
			
		||||
.drinkCartPrice {
 | 
			
		||||
  grid-area: drinkPrice;
 | 
			
		||||
  white-space: nowrap; }
 | 
			
		||||
 | 
			
		||||
.column > .box > .title {
 | 
			
		||||
  padding: 10%; }
 | 
			
		||||
							
								
								
									
										1
									
								
								drinkMe/Client/wwwroot/css/style.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								drinkMe/Client/wwwroot/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 5.3 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								drinkMe/Client/wwwroot/icon-512.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 8.0 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								drinkMe/Client/wwwroot/images/background.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.4 MiB  | 
							
								
								
									
										
											BIN
										
									
								
								drinkMe/Client/wwwroot/images/drinkMe-logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 5.8 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								drinkMe/Client/wwwroot/images/drinks/american-coffee.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 103 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								drinkMe/Client/wwwroot/images/drinks/chocolate.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 40 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								drinkMe/Client/wwwroot/images/drinks/italian-coffee.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 107 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								drinkMe/Client/wwwroot/images/drinks/missing.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 15 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								drinkMe/Client/wwwroot/images/drinks/tea.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 923 KiB  | 
							
								
								
									
										30
									
								
								drinkMe/Client/wwwroot/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,30 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
 | 
			
		||||
<head>
 | 
			
		||||
	<meta charset="utf-8" />
 | 
			
		||||
	<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
 | 
			
		||||
	<title>drinkMe</title>
 | 
			
		||||
	<base href="/" />
 | 
			
		||||
	<link href="css/bulma.min.css" rel="stylesheet" />
 | 
			
		||||
	<link href="css/bulma-steps.min.css" rel="stylesheet" />
 | 
			
		||||
	<link href="css/bulma-divider.min.css" rel="stylesheet" />
 | 
			
		||||
	<link href="css/fontawesome.min.css" rel="stylesheet" />
 | 
			
		||||
	<link href="css/duotone.min.css" rel="stylesheet" />
 | 
			
		||||
	<link href="css/style.min.css" rel="stylesheet" />
 | 
			
		||||
	<link href="manifest.json" rel="manifest" />
 | 
			
		||||
	<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
	<div id="app">
 | 
			
		||||
		<div class="is-flex is-align-items-center loading">
 | 
			
		||||
			<p><img src="images/drinkMe-logo.png" alt="loading logo" /></p>
 | 
			
		||||
			<h1 class="title">Loading...</h1>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
	<script src="_framework/blazor.webassembly.js"></script>
 | 
			
		||||
	<script>navigator.serviceWorker.register('service-worker.js');</script>
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										15
									
								
								drinkMe/Client/wwwroot/manifest.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,15 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "drinkMe",
 | 
			
		||||
  "short_name": "drinkMe",
 | 
			
		||||
  "start_url": "./",
 | 
			
		||||
  "display": "standalone",
 | 
			
		||||
  "background_color": "#ffffff",
 | 
			
		||||
  "theme_color": "#03173d",
 | 
			
		||||
  "icons": [
 | 
			
		||||
    {
 | 
			
		||||
      "src": "icon-512.png",
 | 
			
		||||
      "type": "image/png",
 | 
			
		||||
      "sizes": "512x512"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								drinkMe/Client/wwwroot/service-worker.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,4 @@
 | 
			
		||||
// In development, always fetch from the network and do not enable offline support.
 | 
			
		||||
// This is because caching would make development more difficult (changes would not
 | 
			
		||||
// be reflected on the first load after each change).
 | 
			
		||||
self.addEventListener('fetch', () => { });
 | 
			
		||||
							
								
								
									
										48
									
								
								drinkMe/Client/wwwroot/service-worker.published.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,48 @@
 | 
			
		||||
// Caution! Be sure you understand the caveats before publishing an application with
 | 
			
		||||
// offline support. See https://aka.ms/blazor-offline-considerations
 | 
			
		||||
 | 
			
		||||
self.importScripts('./service-worker-assets.js');
 | 
			
		||||
self.addEventListener('install', event => event.waitUntil(onInstall(event)));
 | 
			
		||||
self.addEventListener('activate', event => event.waitUntil(onActivate(event)));
 | 
			
		||||
self.addEventListener('fetch', event => event.respondWith(onFetch(event)));
 | 
			
		||||
 | 
			
		||||
const cacheNamePrefix = 'offline-cache-';
 | 
			
		||||
const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`;
 | 
			
		||||
const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/ ];
 | 
			
		||||
const offlineAssetsExclude = [ /^service-worker\.js$/ ];
 | 
			
		||||
 | 
			
		||||
async function onInstall(event) {
 | 
			
		||||
    console.info('Service worker: Install');
 | 
			
		||||
 | 
			
		||||
    // Fetch and cache all matching items from the assets manifest
 | 
			
		||||
    const assetsRequests = self.assetsManifest.assets
 | 
			
		||||
        .filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url)))
 | 
			
		||||
        .filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url)))
 | 
			
		||||
        .map(asset => new Request(asset.url, { integrity: asset.hash }));
 | 
			
		||||
    await caches.open(cacheName).then(cache => cache.addAll(assetsRequests));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function onActivate(event) {
 | 
			
		||||
    console.info('Service worker: Activate');
 | 
			
		||||
 | 
			
		||||
    // Delete unused caches
 | 
			
		||||
    const cacheKeys = await caches.keys();
 | 
			
		||||
    await Promise.all(cacheKeys
 | 
			
		||||
        .filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName)
 | 
			
		||||
        .map(key => caches.delete(key)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function onFetch(event) {
 | 
			
		||||
    let cachedResponse = null;
 | 
			
		||||
    if (event.request.method === 'GET') {
 | 
			
		||||
        // For all navigation requests, try to serve index.html from cache
 | 
			
		||||
        // If you need some URLs to be server-rendered, edit the following check to exclude those URLs
 | 
			
		||||
        const shouldServeIndexHtml = event.request.mode === 'navigate';
 | 
			
		||||
 | 
			
		||||
        const request = shouldServeIndexHtml ? 'index.html' : event.request;
 | 
			
		||||
        const cache = await caches.open(cacheName);
 | 
			
		||||
        cachedResponse = await cache.match(request);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return cachedResponse || fetch(event.request);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								drinkMe/Client/wwwroot/webfonts/fa-duotone-900.eot
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										15319
									
								
								drinkMe/Client/wwwroot/webfonts/fa-duotone-900.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.5 MiB  | 
							
								
								
									
										
											BIN
										
									
								
								drinkMe/Client/wwwroot/webfonts/fa-duotone-900.ttf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								drinkMe/Client/wwwroot/webfonts/fa-duotone-900.woff
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								drinkMe/Client/wwwroot/webfonts/fa-duotone-900.woff2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										95
									
								
								drinkMe/Server/Controllers/DrinksController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,95 @@
 | 
			
		||||
using collAnon.Pub.Shared;
 | 
			
		||||
using drinkMe.Server.Models;
 | 
			
		||||
using drinkMe.Server.Services.Interfaces;
 | 
			
		||||
using drinkMe.Shared;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Server.Controllers
 | 
			
		||||
{
 | 
			
		||||
	[ApiController]
 | 
			
		||||
	[Route("api/[controller]/[action]")]
 | 
			
		||||
	public class DrinksController : ControllerBase
 | 
			
		||||
	{
 | 
			
		||||
		readonly IDataService DataService;
 | 
			
		||||
		readonly IPaymentService PaymentService;
 | 
			
		||||
 | 
			
		||||
		public DrinksController(IDataService dataService, IPaymentService paymentService)
 | 
			
		||||
		{
 | 
			
		||||
			DataService = dataService;
 | 
			
		||||
			PaymentService = paymentService;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[HttpGet]
 | 
			
		||||
		public async Task<IActionResult> IsValidDiscountCode([FromQuery] string code)
 | 
			
		||||
		{
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				var validationResult = await DataService.IsValidDiscountCode(code);
 | 
			
		||||
				if (!validationResult.IsValid)
 | 
			
		||||
					return StatusCode(validationResult.StatusCode, validationResult);
 | 
			
		||||
 | 
			
		||||
				var validationData = validationResult.Data as DiscountCode;
 | 
			
		||||
				var resultData = new DiscountCodeViewModel
 | 
			
		||||
				{
 | 
			
		||||
					Code = validationData.Code,
 | 
			
		||||
					ApplicableProducts = validationData.ApplicableProducts,
 | 
			
		||||
					DiscountPercentage = validationData.DiscountPercentage
 | 
			
		||||
				};
 | 
			
		||||
 | 
			
		||||
				return Ok(resultData);
 | 
			
		||||
			}
 | 
			
		||||
			catch (Exception ex)
 | 
			
		||||
			{
 | 
			
		||||
				return BadRequest(new WebResult().Invalidate($"Error at ${nameof(IsValidDiscountCode)}", default, ex));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[HttpGet]
 | 
			
		||||
		public async Task<IActionResult> GetDrinks()
 | 
			
		||||
		{
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				var drinks = DataService.GetDrinks();
 | 
			
		||||
				if (drinks.Count == 0)
 | 
			
		||||
					return NotFound(new WebResult().Invalidate("No drinks available.", StatusCodes.Status404NotFound));
 | 
			
		||||
 | 
			
		||||
				var viewDrinks = drinks.Select(d => new DrinkViewModel
 | 
			
		||||
				{
 | 
			
		||||
					Id = d.Id,
 | 
			
		||||
					Name = d.Name,
 | 
			
		||||
					Description = d.Description,
 | 
			
		||||
					Price = d.Price,
 | 
			
		||||
					PictureName = d.PictureName
 | 
			
		||||
				}).ToArray();
 | 
			
		||||
 | 
			
		||||
				return Ok(viewDrinks);
 | 
			
		||||
			}
 | 
			
		||||
			catch (Exception ex)
 | 
			
		||||
			{
 | 
			
		||||
				return BadRequest(new WebResult().Invalidate($"Error at ${nameof(GetDrinks)}", default, ex));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[HttpPost]
 | 
			
		||||
		public async Task<IActionResult> Pay(PurchaseCart purchaseCart)
 | 
			
		||||
		{
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				var successfulPayment = await PaymentService.Pay(purchaseCart);
 | 
			
		||||
 | 
			
		||||
				if (!successfulPayment)
 | 
			
		||||
					return BadRequest(new WebResult().Invalidate("Payment failed.", StatusCodes.Status400BadRequest));
 | 
			
		||||
 | 
			
		||||
				return Ok();
 | 
			
		||||
			}
 | 
			
		||||
			catch (Exception ex)
 | 
			
		||||
			{
 | 
			
		||||
				return BadRequest(new WebResult().Invalidate($"Error at ${nameof(Pay)}", default, ex));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								drinkMe/Server/Program.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,26 @@
 | 
			
		||||
using Microsoft.AspNetCore.Hosting;
 | 
			
		||||
using Microsoft.Extensions.Configuration;
 | 
			
		||||
using Microsoft.Extensions.Hosting;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Server
 | 
			
		||||
{
 | 
			
		||||
	public class Program
 | 
			
		||||
	{
 | 
			
		||||
		public static void Main(string[] args)
 | 
			
		||||
		{
 | 
			
		||||
			CreateHostBuilder(args).Build().Run();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static IHostBuilder CreateHostBuilder(string[] args) =>
 | 
			
		||||
				Host.CreateDefaultBuilder(args)
 | 
			
		||||
						.ConfigureWebHostDefaults(webBuilder =>
 | 
			
		||||
						{
 | 
			
		||||
							webBuilder.UseStartup<Startup>();
 | 
			
		||||
						});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								drinkMe/Server/Properties/launchSettings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,30 @@
 | 
			
		||||
{
 | 
			
		||||
    "iisSettings": {
 | 
			
		||||
      "windowsAuthentication": false,
 | 
			
		||||
      "anonymousAuthentication": true,
 | 
			
		||||
      "iisExpress": {
 | 
			
		||||
        "applicationUrl": "http://localhost:14962",
 | 
			
		||||
        "sslPort": 44321
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "profiles": {
 | 
			
		||||
      "IIS Express": {
 | 
			
		||||
        "commandName": "IISExpress",
 | 
			
		||||
        "launchBrowser": true,
 | 
			
		||||
        "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
 | 
			
		||||
        "environmentVariables": {
 | 
			
		||||
          "ASPNETCORE_ENVIRONMENT": "Development"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "drinkMe.Server": {
 | 
			
		||||
        "commandName": "Project",
 | 
			
		||||
        "dotnetRunMessages": "true",
 | 
			
		||||
        "launchBrowser": true,
 | 
			
		||||
        "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
 | 
			
		||||
        "applicationUrl": "https://localhost:5001;http://localhost:5000",
 | 
			
		||||
        "environmentVariables": {
 | 
			
		||||
          "ASPNETCORE_ENVIRONMENT": "Development"
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
							
								
								
									
										69
									
								
								drinkMe/Server/Services/DataService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,69 @@
 | 
			
		||||
using collAnon.Pub.Shared;
 | 
			
		||||
using drinkMe.Server.Models;
 | 
			
		||||
using drinkMe.Server.Services.Interfaces;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Server.Services
 | 
			
		||||
{
 | 
			
		||||
	public class DataService : IDataService
 | 
			
		||||
	{
 | 
			
		||||
		List<DiscountCode> DiscountCodes = new List<DiscountCode>
 | 
			
		||||
		{
 | 
			
		||||
			new DiscountCode{ Code ="ITS_M0NDAY", DiscountPercentage = 15, ApplicableProducts = new[] { 2 } },
 | 
			
		||||
			new DiscountCode{ Code ="ITS_K1NDA_COLD", DiscountPercentage = 30, ApplicableProducts = new[] { 4 } },
 | 
			
		||||
			new DiscountCode{ Code ="I_NEED_R3LAX", DiscountPercentage = 25, ApplicableProducts = new[] { 3 } },
 | 
			
		||||
			new DiscountCode{ Code ="D1AMOND_HANDS", DiscountPercentage = 100, ApplicableProducts = new[] { 1 } }
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		public List<Drink> GetDrinks() =>
 | 
			
		||||
			new List<Drink>
 | 
			
		||||
			{
 | 
			
		||||
				new Drink
 | 
			
		||||
				{
 | 
			
		||||
						Id = 1,
 | 
			
		||||
						Name = "Italian Coffee",
 | 
			
		||||
						Description = "Short but strong.",
 | 
			
		||||
						Price = 3.50f,
 | 
			
		||||
						PictureName = "italian-coffee.jpg"
 | 
			
		||||
				},
 | 
			
		||||
				new Drink
 | 
			
		||||
				{
 | 
			
		||||
						Id = 2,
 | 
			
		||||
						Name = "American Coffee",
 | 
			
		||||
						Description = "Dilluted in water to a cup size, not as strong as the italian one.",
 | 
			
		||||
						Price = 3.0f,
 | 
			
		||||
						PictureName = "american-coffee.jpg"
 | 
			
		||||
				},
 | 
			
		||||
				new Drink
 | 
			
		||||
				{
 | 
			
		||||
						Id = 3,
 | 
			
		||||
						Name = "Tea",
 | 
			
		||||
						Description = "Black Earl Grey, good alternative to coffee from time to time.",
 | 
			
		||||
						Price = 2.0f,
 | 
			
		||||
						PictureName = "tea.png"
 | 
			
		||||
				},
 | 
			
		||||
				new Drink
 | 
			
		||||
				{
 | 
			
		||||
						Id = 4,
 | 
			
		||||
						Name = "Chocolate",
 | 
			
		||||
						Description = "Your dose of sugar and calories, just don't loose the hang.",
 | 
			
		||||
						Price = 4.0f,
 | 
			
		||||
						PictureName = "chocolate.jpg"
 | 
			
		||||
				}
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
		public async Task<WebResult> IsValidDiscountCode(string code)
 | 
			
		||||
		{
 | 
			
		||||
			var result = new WebResult();
 | 
			
		||||
			if (!DiscountCodes.Any(dc => dc.Code == code))
 | 
			
		||||
				return result.Invalidate("Invalid discount code.", StatusCodes.Status404NotFound);
 | 
			
		||||
 | 
			
		||||
			result.Data = DiscountCodes.First(dc => dc.Code == code);
 | 
			
		||||
			return result;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								drinkMe/Server/Services/Interfaces/IDataService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,13 @@
 | 
			
		||||
using collAnon.Pub.Shared;
 | 
			
		||||
using drinkMe.Server.Models;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Server.Services.Interfaces
 | 
			
		||||
{
 | 
			
		||||
	public interface IDataService
 | 
			
		||||
	{
 | 
			
		||||
		List<Drink> GetDrinks();
 | 
			
		||||
		Task<WebResult> IsValidDiscountCode(string code);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								drinkMe/Server/Services/Interfaces/IPaymentService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,13 @@
 | 
			
		||||
using drinkMe.Shared;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Server.Services.Interfaces
 | 
			
		||||
{
 | 
			
		||||
	public interface IPaymentService
 | 
			
		||||
	{
 | 
			
		||||
		Task<bool> Pay(PurchaseCart purchaseCart);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								drinkMe/Server/Services/PaymentService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,18 @@
 | 
			
		||||
using drinkMe.Server.Services.Interfaces;
 | 
			
		||||
using drinkMe.Shared;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Server.Services
 | 
			
		||||
{
 | 
			
		||||
	public class PaymentService : IPaymentService
 | 
			
		||||
	{
 | 
			
		||||
		public async Task<bool> Pay(PurchaseCart purchaseCart)
 | 
			
		||||
		{
 | 
			
		||||
			//completing the purchase
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										66
									
								
								drinkMe/Server/Startup.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,66 @@
 | 
			
		||||
using drinkMe.Server.Services;
 | 
			
		||||
using drinkMe.Server.Services.Interfaces;
 | 
			
		||||
using Microsoft.AspNetCore.Builder;
 | 
			
		||||
using Microsoft.AspNetCore.Hosting;
 | 
			
		||||
using Microsoft.AspNetCore.HttpsPolicy;
 | 
			
		||||
using Microsoft.AspNetCore.ResponseCompression;
 | 
			
		||||
using Microsoft.Extensions.Configuration;
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
using Microsoft.Extensions.Hosting;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Server
 | 
			
		||||
{
 | 
			
		||||
	public class Startup
 | 
			
		||||
	{
 | 
			
		||||
		public Startup(IConfiguration configuration)
 | 
			
		||||
		{
 | 
			
		||||
			Configuration = configuration;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public IConfiguration Configuration { get; }
 | 
			
		||||
 | 
			
		||||
		// This method gets called by the runtime. Use this method to add services to the container.
 | 
			
		||||
		// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
 | 
			
		||||
		public void ConfigureServices(IServiceCollection services)
 | 
			
		||||
		{
 | 
			
		||||
			services.AddScoped<IDataService, DataService>()
 | 
			
		||||
				.AddScoped<IPaymentService, PaymentService>();
 | 
			
		||||
 | 
			
		||||
			services.AddControllers().AddJsonOptions(options =>
 | 
			
		||||
			{
 | 
			
		||||
				options.JsonSerializerOptions.IgnoreReadOnlyFields = true;
 | 
			
		||||
				options.JsonSerializerOptions.IgnoreReadOnlyProperties = true;
 | 
			
		||||
			});
 | 
			
		||||
			services.AddRazorPages();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
 | 
			
		||||
		public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
 | 
			
		||||
		{
 | 
			
		||||
			if (env.IsDevelopment())
 | 
			
		||||
			{
 | 
			
		||||
				app.UseDeveloperExceptionPage();
 | 
			
		||||
				app.UseWebAssemblyDebugging();
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
 | 
			
		||||
				app.UseHsts();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			app.UseHttpsRedirection();
 | 
			
		||||
			app.UseBlazorFrameworkFiles();
 | 
			
		||||
			app.UseStaticFiles();
 | 
			
		||||
 | 
			
		||||
			app.UseRouting();
 | 
			
		||||
 | 
			
		||||
			app.UseEndpoints(endpoints =>
 | 
			
		||||
			{
 | 
			
		||||
				endpoints.MapRazorPages();
 | 
			
		||||
				endpoints.MapControllers();
 | 
			
		||||
				endpoints.MapFallbackToFile("index.html");
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								drinkMe/Server/appsettings.Development.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "Logging": {
 | 
			
		||||
    "LogLevel": {
 | 
			
		||||
      "Default": "Information",
 | 
			
		||||
      "Microsoft": "Warning",
 | 
			
		||||
      "Microsoft.Hosting.Lifetime": "Information"
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								drinkMe/Server/appsettings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,10 @@
 | 
			
		||||
{
 | 
			
		||||
  "Logging": {
 | 
			
		||||
    "LogLevel": {
 | 
			
		||||
      "Default": "Information",
 | 
			
		||||
      "Microsoft": "Warning",
 | 
			
		||||
      "Microsoft.Hosting.Lifetime": "Information"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
"AllowedHosts": "*"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								drinkMe/Server/drinkMe.Server.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,18 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk.Web">
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <TargetFramework>net5.0</TargetFramework>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="5.0.5" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <ProjectReference Include="..\Client\drinkMe.Client.csproj" />
 | 
			
		||||
    <ProjectReference Include="..\drinkMe.Server.Models\drinkMe.Server.Models.csproj" />
 | 
			
		||||
    <ProjectReference Include="..\Shared\drinkMe.Shared.csproj" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
							
								
								
									
										14
									
								
								drinkMe/Shared/CartItem.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,14 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Shared
 | 
			
		||||
{
 | 
			
		||||
	public class CartItem
 | 
			
		||||
	{
 | 
			
		||||
		public int Id { get; set; }
 | 
			
		||||
		public int Quantity { get; set; } = 1;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								drinkMe/Shared/DiscountCodeViewModel.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,9 @@
 | 
			
		||||
namespace drinkMe.Shared
 | 
			
		||||
{
 | 
			
		||||
	public class DiscountCodeViewModel
 | 
			
		||||
	{
 | 
			
		||||
		public string Code { get; set; }
 | 
			
		||||
		public float DiscountPercentage  { get; set; }
 | 
			
		||||
		public int[] ApplicableProducts { get; set; }
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								drinkMe/Shared/DrinkViewModel.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,18 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Shared
 | 
			
		||||
{
 | 
			
		||||
	public class DrinkViewModel
 | 
			
		||||
	{
 | 
			
		||||
		public int Id { get; set; }
 | 
			
		||||
		public string Name { get; set; } = "Missing name";
 | 
			
		||||
		public string Description { get; set; } = "Missing description";
 | 
			
		||||
		public string PictureName { get; set; } = "missing.png";
 | 
			
		||||
		[Range(1, 20)]
 | 
			
		||||
		public int Quantity { get; set; } = 1;
 | 
			
		||||
		public float Price { get; set; } = 0;
 | 
			
		||||
 | 
			
		||||
		public bool IsInTheCart { get; set; } = false;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								drinkMe/Shared/PurchaseCart.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,15 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Shared
 | 
			
		||||
{
 | 
			
		||||
	public class PurchaseCart
 | 
			
		||||
	{
 | 
			
		||||
		public bool IsPayedWithCash { get; set; } = false;
 | 
			
		||||
		public List<CartItem> PurchasingItems { get; set; } = new List<CartItem>();
 | 
			
		||||
		public List<DiscountCodeViewModel> Discounts { get; set; } = new List<DiscountCodeViewModel>();
 | 
			
		||||
		public string CreditCardNumber { get; set; }
 | 
			
		||||
		public string CreditCardExpirationMonth { get; set; }
 | 
			
		||||
		public string CreditCardExpirationYear { get; set; }
 | 
			
		||||
		public string CreditCardCVVCode { get; set; }
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								drinkMe/Shared/WebResult.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,47 @@
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Text.Json.Serialization;
 | 
			
		||||
 | 
			
		||||
namespace collAnon.Pub.Shared
 | 
			
		||||
{
 | 
			
		||||
	public class WebResult
 | 
			
		||||
	{
 | 
			
		||||
		public WebResult Invalidate<T>(string errorMessage, int statusCode = StatusCodes.Status400BadRequest, Exception exception = null, T defaultData = default)
 | 
			
		||||
		{
 | 
			
		||||
			IsValid = false;
 | 
			
		||||
			ErrorMessage = errorMessage;
 | 
			
		||||
			StatusCode = statusCode;
 | 
			
		||||
			Exception = exception;
 | 
			
		||||
			Data = defaultData;
 | 
			
		||||
			return this;
 | 
			
		||||
		}
 | 
			
		||||
		public WebResult Invalidate(string errorMessage, int statusCode = StatusCodes.Status400BadRequest, Exception exception = null)
 | 
			
		||||
		{
 | 
			
		||||
			IsValid = false;
 | 
			
		||||
			ErrorMessage = errorMessage;
 | 
			
		||||
			StatusCode = statusCode;
 | 
			
		||||
			Exception = exception;
 | 
			
		||||
			return this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public WebResult Invalidate(WebResult result)
 | 
			
		||||
		{
 | 
			
		||||
			IsValid = result.IsValid;
 | 
			
		||||
			ErrorMessage = result.ErrorMessage;
 | 
			
		||||
			StatusCode = result.StatusCode;
 | 
			
		||||
			Exception = result.Exception;
 | 
			
		||||
			Data = result.Data;
 | 
			
		||||
			return this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public int StatusCode { get; set; } = StatusCodes.Status200OK;
 | 
			
		||||
		public string ErrorMessage { get; set; }
 | 
			
		||||
 | 
			
		||||
		public bool IsValid { get; set; } = true;
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public object Data { get; set; }
 | 
			
		||||
		[JsonIgnore]
 | 
			
		||||
		public Exception Exception { get; set; }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								drinkMe/Shared/drinkMe.Shared.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,14 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <TargetFramework>net5.0</TargetFramework>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <SupportedPlatform Include="browser" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
</Project>
 | 
			
		||||
							
								
								
									
										25
									
								
								drinkMe/drinkMe.Client.Models/CreditCardForm.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,25 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Client.Models
 | 
			
		||||
{
 | 
			
		||||
	public class CreditCardForm
 | 
			
		||||
	{
 | 
			
		||||
		[Required, Display(Name = "Credit card number"),
 | 
			
		||||
			MaxLength(16), MinLength(16)]
 | 
			
		||||
		public string Number { get; set; }
 | 
			
		||||
		[Required, Display(Name = "Expiration month"),
 | 
			
		||||
			Range(2, 2), RegularExpression(@"^((0[1-9])|(1[0-2]))$", ErrorMessage = "Invalid month")]
 | 
			
		||||
		public string ExpirationMonth { get; set; }
 | 
			
		||||
		[Required, Display(Name = "Expiration year"),
 | 
			
		||||
			Range(2, 2), RegularExpression(@"^(\d{2})$", ErrorMessage = "Invalid year")]
 | 
			
		||||
		public string ExpirationYear { get; set; }
 | 
			
		||||
		[Required, Display(Name = "CVV code"),
 | 
			
		||||
			Range(3, 3), RegularExpression(@"^(\d{3})$", ErrorMessage = "Invalid CVV code")]
 | 
			
		||||
		public string CVVCode { get; set; }
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								drinkMe/drinkMe.Client.Models/DiscountForm.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,11 @@
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace drinkMe.Client.Models
 | 
			
		||||
{
 | 
			
		||||
	public class DiscountForm
 | 
			
		||||
	{
 | 
			
		||||
		[Required,
 | 
			
		||||
			Display(Name = "Discount code")]
 | 
			
		||||
		public string DiscountCode { get; set; }
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								drinkMe/drinkMe.Client.Models/PaymentMethod.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,9 @@
 | 
			
		||||
namespace drinkMe.Client.Models
 | 
			
		||||
{
 | 
			
		||||
	public enum PaymentMethod
 | 
			
		||||
	{
 | 
			
		||||
		Undefined,
 | 
			
		||||
		Cash,
 | 
			
		||||
		CreditCard
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								drinkMe/drinkMe.Client.Models/PurchaseStep.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,9 @@
 | 
			
		||||
namespace drinkMe.Client.Models
 | 
			
		||||
{
 | 
			
		||||
	public enum PurchaseStep
 | 
			
		||||
	{
 | 
			
		||||
		ChoosingProducts,
 | 
			
		||||
		PaymentSetup,
 | 
			
		||||
		SuccessfulPurchase
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,7 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <TargetFramework>net5.0</TargetFramework>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
							
								
								
									
										9
									
								
								drinkMe/drinkMe.Server.Models/DiscountCode.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,9 @@
 | 
			
		||||
namespace drinkMe.Server.Models
 | 
			
		||||
{
 | 
			
		||||
	public class DiscountCode
 | 
			
		||||
	{
 | 
			
		||||
		public string Code { get; set; }
 | 
			
		||||
		public float DiscountPercentage { get; set; }
 | 
			
		||||
		public int[] ApplicableProducts { get; set; }
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								drinkMe/drinkMe.Server.Models/Drink.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,11 @@
 | 
			
		||||
namespace drinkMe.Server.Models
 | 
			
		||||
{
 | 
			
		||||
	public class Drink
 | 
			
		||||
	{
 | 
			
		||||
		public int Id { get; set; }
 | 
			
		||||
		public string Name { get; set; } = "Missing name";
 | 
			
		||||
		public string Description { get; set; } = "Missing description";
 | 
			
		||||
		public float Price { get; set; } = 0;
 | 
			
		||||
		public string PictureName { get; set; } = "missing.png";
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,7 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <TargetFramework>net5.0</TargetFramework>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||