init webui to wrappng eshoponcontainers
This commit is contained in:
parent
1807e952cf
commit
a6ff3aa9a9
@ -36,6 +36,7 @@ COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment
|
|||||||
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
||||||
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
||||||
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
||||||
|
COPY "Web/WebUI/WebUI.csproj" "Web/WebUI/WebUI.csproj"
|
||||||
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
||||||
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
||||||
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
||||||
|
@ -36,6 +36,7 @@ COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment
|
|||||||
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
||||||
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
||||||
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
||||||
|
COPY "Web/WebUI/WebUI.csproj" "Web/WebUI/WebUI.csproj"
|
||||||
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
||||||
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
||||||
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
||||||
|
@ -36,6 +36,7 @@ COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment
|
|||||||
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
||||||
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
||||||
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
||||||
|
COPY "Web/WebUI/WebUI.csproj" "Web/WebUI/WebUI.csproj"
|
||||||
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
||||||
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
||||||
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
||||||
|
@ -37,6 +37,7 @@ COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment
|
|||||||
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
||||||
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
||||||
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
||||||
|
COPY "Web/WebUI/WebUI.csproj" "Web/WebUI/WebUI.csproj"
|
||||||
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
||||||
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
||||||
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
||||||
|
@ -86,12 +86,49 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
|
|||||||
AllowAccessTokensViaBrowser = true
|
AllowAccessTokensViaBrowser = true
|
||||||
},
|
},
|
||||||
new Client
|
new Client
|
||||||
|
{
|
||||||
|
ClientId = "ui",
|
||||||
|
ClientName = "UI Client",
|
||||||
|
ClientSecrets = new List<Secret>
|
||||||
|
{
|
||||||
|
|
||||||
|
new Secret("secret".Sha256())
|
||||||
|
},
|
||||||
|
ClientUri = $"{clientsUrl["UI"]}", // public uri of the client
|
||||||
|
AllowedGrantTypes = GrantTypes.Hybrid,
|
||||||
|
AllowAccessTokensViaBrowser = false,
|
||||||
|
RequireConsent = false,
|
||||||
|
AllowOfflineAccess = true,
|
||||||
|
AlwaysIncludeUserClaimsInIdToken = true,
|
||||||
|
RedirectUris = new List<string>
|
||||||
|
{
|
||||||
|
$"{clientsUrl["UI"]}/signin-oidc"
|
||||||
|
},
|
||||||
|
PostLogoutRedirectUris = new List<string>
|
||||||
|
{
|
||||||
|
$"{clientsUrl["UI"]}/signout-callback-oidc"
|
||||||
|
},
|
||||||
|
AllowedScopes = new List<string>
|
||||||
|
{
|
||||||
|
IdentityServerConstants.StandardScopes.OpenId,
|
||||||
|
IdentityServerConstants.StandardScopes.Profile,
|
||||||
|
IdentityServerConstants.StandardScopes.OfflineAccess,
|
||||||
|
"orders",
|
||||||
|
"basket",
|
||||||
|
"webshoppingagg",
|
||||||
|
"orders.signalrhub",
|
||||||
|
"webhooks"
|
||||||
|
},
|
||||||
|
AccessTokenLifetime = 60*60*2, // 2 hours
|
||||||
|
IdentityTokenLifetime= 60*60*2 // 2 hours
|
||||||
|
},
|
||||||
|
new Client
|
||||||
{
|
{
|
||||||
ClientId = "mvc",
|
ClientId = "mvc",
|
||||||
ClientName = "MVC Client",
|
ClientName = "MVC Client",
|
||||||
ClientSecrets = new List<Secret>
|
ClientSecrets = new List<Secret>
|
||||||
{
|
{
|
||||||
|
|
||||||
new Secret("secret".Sha256())
|
new Secret("secret".Sha256())
|
||||||
},
|
},
|
||||||
ClientUri = $"{clientsUrl["Mvc"]}", // public uri of the client
|
ClientUri = $"{clientsUrl["Mvc"]}", // public uri of the client
|
||||||
|
@ -11,6 +11,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
|
|||||||
var clientUrls = new Dictionary<string, string>();
|
var clientUrls = new Dictionary<string, string>();
|
||||||
|
|
||||||
clientUrls.Add("Mvc", configuration.GetValue<string>("MvcClient"));
|
clientUrls.Add("Mvc", configuration.GetValue<string>("MvcClient"));
|
||||||
|
clientUrls.Add("UI", configuration.GetValue<string>("UIClient"));
|
||||||
clientUrls.Add("Spa", configuration.GetValue<string>("SpaClient"));
|
clientUrls.Add("Spa", configuration.GetValue<string>("SpaClient"));
|
||||||
clientUrls.Add("Xamarin", configuration.GetValue<string>("XamarinCallback"));
|
clientUrls.Add("Xamarin", configuration.GetValue<string>("XamarinCallback"));
|
||||||
clientUrls.Add("BasketApi", configuration.GetValue<string>("BasketApiClient"));
|
clientUrls.Add("BasketApi", configuration.GetValue<string>("BasketApiClient"));
|
||||||
|
@ -36,6 +36,7 @@ COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment
|
|||||||
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
||||||
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
||||||
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
||||||
|
COPY "Web/WebUI/WebUI.csproj" "Web/WebUI/WebUI.csproj"
|
||||||
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
||||||
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
||||||
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
"ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.IdentityDb;User Id=sa;Password=Pass@word;",
|
"ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.IdentityDb;User Id=sa;Password=Pass@word;",
|
||||||
"IsClusterEnv": "False",
|
"IsClusterEnv": "False",
|
||||||
"MvcClient": "http://localhost:5100",
|
"MvcClient": "http://localhost:5100",
|
||||||
|
"UIClient": "http://localhost:5300",
|
||||||
"SpaClient": "http://localhost:5104",
|
"SpaClient": "http://localhost:5104",
|
||||||
"XamarinCallback": "http://localhost:5105/xamarincallback",
|
"XamarinCallback": "http://localhost:5105/xamarincallback",
|
||||||
"UseCustomizationData": false,
|
"UseCustomizationData": false,
|
||||||
|
@ -36,6 +36,7 @@ COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment
|
|||||||
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
||||||
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
||||||
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
||||||
|
COPY "Web/WebUI/WebUI.csproj" "Web/WebUI/WebUI.csproj"
|
||||||
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
||||||
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
||||||
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
||||||
|
@ -36,6 +36,7 @@ COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment
|
|||||||
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
||||||
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
||||||
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
||||||
|
COPY "Web/WebUI/WebUI.csproj" "Web/WebUI/WebUI.csproj"
|
||||||
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
||||||
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
||||||
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
||||||
|
@ -36,6 +36,7 @@ COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment
|
|||||||
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
||||||
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
||||||
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
||||||
|
COPY "Web/WebUI/WebUI.csproj" "Web/WebUI/WebUI.csproj"
|
||||||
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
||||||
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
||||||
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
||||||
|
@ -36,6 +36,7 @@ COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment
|
|||||||
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
||||||
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
||||||
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
||||||
|
COPY "Web/WebUI/WebUI.csproj" "Web/WebUI/WebUI.csproj"
|
||||||
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
||||||
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
||||||
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
||||||
|
@ -36,6 +36,7 @@ COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment
|
|||||||
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
||||||
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
||||||
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
||||||
|
COPY "Web/WebUI/WebUI.csproj" "Web/WebUI/WebUI.csproj"
|
||||||
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
||||||
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
||||||
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
||||||
|
@ -36,6 +36,7 @@ COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment
|
|||||||
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
||||||
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
||||||
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
||||||
|
COPY "Web/WebUI/WebUI.csproj" "Web/WebUI/WebUI.csproj"
|
||||||
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
||||||
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
||||||
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
|
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
|
||||||
-->
|
-->
|
||||||
<system.webServer>
|
<system.webServer>
|
||||||
<handlers>
|
<handlers>
|
||||||
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
|
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
|
||||||
</handlers>
|
</handlers>
|
||||||
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
|
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" hostingModel="InProcess" />
|
||||||
</system.webServer>
|
</system.webServer>
|
||||||
</configuration>
|
</configuration>
|
@ -47,6 +47,7 @@ COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment
|
|||||||
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
||||||
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
||||||
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
||||||
|
COPY "Web/WebUI/WebUI.csproj" "Web/WebUI/WebUI.csproj"
|
||||||
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
||||||
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
||||||
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
||||||
|
@ -36,6 +36,7 @@ COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment
|
|||||||
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
||||||
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
||||||
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
||||||
|
COPY "Web/WebUI/WebUI.csproj" "Web/WebUI/WebUI.csproj"
|
||||||
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
||||||
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
||||||
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
||||||
|
14
src/Web/WebUI/.dockerignore
Normal file
14
src/Web/WebUI/.dockerignore
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
.dockerignore
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.vs
|
||||||
|
.vscode
|
||||||
|
**/*.*proj.user
|
||||||
|
**/azds.yaml
|
||||||
|
**/bin
|
||||||
|
**/charts
|
||||||
|
**/Dockerfile
|
||||||
|
**/Dockerfile.develop
|
||||||
|
**/obj
|
||||||
|
**/secrets.dev.yaml
|
||||||
|
**/values.dev.yaml
|
29
src/Web/WebUI/AppSettings.cs
Normal file
29
src/Web/WebUI/AppSettings.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI;
|
||||||
|
|
||||||
|
public class AppSettings
|
||||||
|
{
|
||||||
|
//public Connectionstrings ConnectionStrings { get; set; }
|
||||||
|
public string PurchaseUrl { get; set; }
|
||||||
|
public string SignalrHubUrl { get; set; }
|
||||||
|
public bool ActivateCampaignDetailFunction { get; set; }
|
||||||
|
public Logging Logging { get; set; }
|
||||||
|
public bool UseCustomizationData { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Connectionstrings
|
||||||
|
{
|
||||||
|
public string DefaultConnection { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Logging
|
||||||
|
{
|
||||||
|
public bool IncludeScopes { get; set; }
|
||||||
|
public Loglevel LogLevel { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Loglevel
|
||||||
|
{
|
||||||
|
public string Default { get; set; }
|
||||||
|
public string System { get; set; }
|
||||||
|
public string Microsoft { get; set; }
|
||||||
|
}
|
42
src/Web/WebUI/Controllers/AccountController.cs
Normal file
42
src/Web/WebUI/Controllers/AccountController.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.Controllers;
|
||||||
|
|
||||||
|
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)]
|
||||||
|
public class AccountController : Controller
|
||||||
|
{
|
||||||
|
private readonly ILogger<AccountController> _logger;
|
||||||
|
|
||||||
|
public AccountController(ILogger<AccountController> logger)
|
||||||
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)]
|
||||||
|
public async Task<IActionResult> SignIn(string returnUrl)
|
||||||
|
{
|
||||||
|
var user = User as ClaimsPrincipal;
|
||||||
|
var token = await HttpContext.GetTokenAsync("access_token");
|
||||||
|
|
||||||
|
_logger.LogInformation("----- User {@User} authenticated into {AppName}", user, Program.AppName);
|
||||||
|
|
||||||
|
if (token != null)
|
||||||
|
{
|
||||||
|
ViewData["access_token"] = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Catalog" because UrlHelper doesn't support nameof() for controllers
|
||||||
|
// https://github.com/aspnet/Mvc/issues/5853
|
||||||
|
return RedirectToAction(nameof(CatalogController.Index), "Catalog");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> Signout()
|
||||||
|
{
|
||||||
|
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||||
|
await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);
|
||||||
|
|
||||||
|
// "Catalog" because UrlHelper doesn't support nameof() for controllers
|
||||||
|
// https://github.com/aspnet/Mvc/issues/5853
|
||||||
|
var homeUrl = Url.Action(nameof(CatalogController.Index), "Catalog");
|
||||||
|
return new SignOutResult(OpenIdConnectDefaults.AuthenticationScheme,
|
||||||
|
new AspNetCore.Authentication.AuthenticationProperties { RedirectUri = homeUrl });
|
||||||
|
}
|
||||||
|
}
|
79
src/Web/WebUI/Controllers/CartController.cs
Normal file
79
src/Web/WebUI/Controllers/CartController.cs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.Controllers;
|
||||||
|
|
||||||
|
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)]
|
||||||
|
public class CartController : Controller
|
||||||
|
{
|
||||||
|
private readonly IBasketService _basketSvc;
|
||||||
|
private readonly ICatalogService _catalogSvc;
|
||||||
|
private readonly IIdentityParser<ApplicationUser> _appUserParser;
|
||||||
|
|
||||||
|
public CartController(IBasketService basketSvc, ICatalogService catalogSvc, IIdentityParser<ApplicationUser> appUserParser)
|
||||||
|
{
|
||||||
|
_basketSvc = basketSvc;
|
||||||
|
_catalogSvc = catalogSvc;
|
||||||
|
_appUserParser = appUserParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> Index()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var user = _appUserParser.Parse(HttpContext.User);
|
||||||
|
var vm = await _basketSvc.GetBasket(user);
|
||||||
|
|
||||||
|
return View(vm);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
HandleException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Index(Dictionary<string, int> quantities, string action)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var user = _appUserParser.Parse(HttpContext.User);
|
||||||
|
var basket = await _basketSvc.SetQuantities(user, quantities);
|
||||||
|
if (action == "[ Checkout ]")
|
||||||
|
{
|
||||||
|
return RedirectToAction("Create", "Order");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
HandleException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> AddToCart(CatalogItem productDetails)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (productDetails?.Id != null)
|
||||||
|
{
|
||||||
|
var user = _appUserParser.Parse(HttpContext.User);
|
||||||
|
await _basketSvc.AddItemToBasket(user, productDetails.Id);
|
||||||
|
}
|
||||||
|
return RedirectToAction("Index", "Catalog");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Catch error when Basket.api is in circuit-opened mode
|
||||||
|
HandleException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return RedirectToAction("Index", "Catalog", new { errorMsg = ViewBag.BasketInoperativeMsg });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleException(Exception ex)
|
||||||
|
{
|
||||||
|
ViewBag.BasketInoperativeMsg = $"Basket Service is inoperative {ex.GetType().Name} - {ex.Message}";
|
||||||
|
}
|
||||||
|
}
|
37
src/Web/WebUI/Controllers/CatalogController.cs
Normal file
37
src/Web/WebUI/Controllers/CatalogController.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.Controllers;
|
||||||
|
|
||||||
|
public class CatalogController : Controller
|
||||||
|
{
|
||||||
|
private ICatalogService _catalogSvc;
|
||||||
|
|
||||||
|
public CatalogController(ICatalogService catalogSvc) =>
|
||||||
|
_catalogSvc = catalogSvc;
|
||||||
|
|
||||||
|
public async Task<IActionResult> Index(int? BrandFilterApplied, int? TypesFilterApplied, int? page, [FromQuery] string errorMsg)
|
||||||
|
{
|
||||||
|
var itemsPage = 9;
|
||||||
|
var catalog = await _catalogSvc.GetCatalogItems(page ?? 0, itemsPage, BrandFilterApplied, TypesFilterApplied);
|
||||||
|
var vm = new IndexViewModel()
|
||||||
|
{
|
||||||
|
CatalogItems = catalog.Data,
|
||||||
|
Brands = await _catalogSvc.GetBrands(),
|
||||||
|
Types = await _catalogSvc.GetTypes(),
|
||||||
|
BrandFilterApplied = BrandFilterApplied ?? 0,
|
||||||
|
TypesFilterApplied = TypesFilterApplied ?? 0,
|
||||||
|
PaginationInfo = new PaginationInfo()
|
||||||
|
{
|
||||||
|
ActualPage = page ?? 0,
|
||||||
|
ItemsPerPage = catalog.Data.Count,
|
||||||
|
TotalItems = catalog.Count,
|
||||||
|
TotalPages = (int)Math.Ceiling(((decimal)catalog.Count / itemsPage))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
vm.PaginationInfo.Next = (vm.PaginationInfo.ActualPage == vm.PaginationInfo.TotalPages - 1) ? "is-disabled" : "";
|
||||||
|
vm.PaginationInfo.Previous = (vm.PaginationInfo.ActualPage == 0) ? "is-disabled" : "";
|
||||||
|
|
||||||
|
ViewBag.BasketInoperativeMsg = errorMsg;
|
||||||
|
|
||||||
|
return View(vm);
|
||||||
|
}
|
||||||
|
}
|
6
src/Web/WebUI/Controllers/ErrorController.cs
Normal file
6
src/Web/WebUI/Controllers/ErrorController.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace WebUI.Controllers;
|
||||||
|
|
||||||
|
public class ErrorController : Controller
|
||||||
|
{
|
||||||
|
public IActionResult Error() => View();
|
||||||
|
}
|
75
src/Web/WebUI/Controllers/OrderController.cs
Normal file
75
src/Web/WebUI/Controllers/OrderController.cs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.Controllers;
|
||||||
|
|
||||||
|
using Microsoft.eShopOnContainers.WebUI.ViewModels;
|
||||||
|
|
||||||
|
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)]
|
||||||
|
public class OrderController : Controller
|
||||||
|
{
|
||||||
|
private IOrderingService _orderSvc;
|
||||||
|
private IBasketService _basketSvc;
|
||||||
|
private readonly IIdentityParser<ApplicationUser> _appUserParser;
|
||||||
|
public OrderController(IOrderingService orderSvc, IBasketService basketSvc, IIdentityParser<ApplicationUser> appUserParser)
|
||||||
|
{
|
||||||
|
_appUserParser = appUserParser;
|
||||||
|
_orderSvc = orderSvc;
|
||||||
|
_basketSvc = basketSvc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> Create()
|
||||||
|
{
|
||||||
|
|
||||||
|
var user = _appUserParser.Parse(HttpContext.User);
|
||||||
|
var order = await _basketSvc.GetOrderDraft(user.Id);
|
||||||
|
var vm = _orderSvc.MapUserInfoIntoOrder(user, order);
|
||||||
|
vm.CardExpirationShortFormat();
|
||||||
|
|
||||||
|
return View(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Checkout(Order model)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (ModelState.IsValid)
|
||||||
|
{
|
||||||
|
var user = _appUserParser.Parse(HttpContext.User);
|
||||||
|
var basket = _orderSvc.MapOrderToBasket(model);
|
||||||
|
|
||||||
|
await _basketSvc.Checkout(basket);
|
||||||
|
|
||||||
|
//Redirect to historic list.
|
||||||
|
return RedirectToAction("Index");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ModelState.AddModelError("Error", $"It was not possible to create a new order, please try later on ({ex.GetType().Name} - {ex.Message})");
|
||||||
|
}
|
||||||
|
|
||||||
|
return View("Create", model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> Cancel(string orderId)
|
||||||
|
{
|
||||||
|
await _orderSvc.CancelOrder(orderId);
|
||||||
|
|
||||||
|
//Redirect to historic list.
|
||||||
|
return RedirectToAction("Index");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> Detail(string orderId)
|
||||||
|
{
|
||||||
|
var user = _appUserParser.Parse(HttpContext.User);
|
||||||
|
|
||||||
|
var order = await _orderSvc.GetOrder(user, orderId);
|
||||||
|
return View(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> Index(Order item)
|
||||||
|
{
|
||||||
|
var user = _appUserParser.Parse(HttpContext.User);
|
||||||
|
var vm = await _orderSvc.GetMyOrders(user);
|
||||||
|
return View(vm);
|
||||||
|
}
|
||||||
|
}
|
32
src/Web/WebUI/Controllers/OrderManagementController.cs
Normal file
32
src/Web/WebUI/Controllers/OrderManagementController.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
namespace WebUI.Controllers;
|
||||||
|
|
||||||
|
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)]
|
||||||
|
public class OrderManagementController : Controller
|
||||||
|
{
|
||||||
|
private IOrderingService _orderSvc;
|
||||||
|
private readonly IIdentityParser<ApplicationUser> _appUserParser;
|
||||||
|
public OrderManagementController(IOrderingService orderSvc, IIdentityParser<ApplicationUser> appUserParser)
|
||||||
|
{
|
||||||
|
_appUserParser = appUserParser;
|
||||||
|
_orderSvc = orderSvc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> Index()
|
||||||
|
{
|
||||||
|
var user = _appUserParser.Parse(HttpContext.User);
|
||||||
|
var vm = await _orderSvc.GetMyOrders(user);
|
||||||
|
|
||||||
|
return View(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> OrderProcess(string orderId, string actionCode)
|
||||||
|
{
|
||||||
|
if (OrderProcessAction.Ship.Code == actionCode)
|
||||||
|
{
|
||||||
|
await _orderSvc.ShipOrder(orderId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return RedirectToAction("Index");
|
||||||
|
}
|
||||||
|
}
|
52
src/Web/WebUI/Controllers/TestController.cs
Normal file
52
src/Web/WebUI/Controllers/TestController.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
namespace WebUI.Controllers;
|
||||||
|
|
||||||
|
class TestPayload
|
||||||
|
{
|
||||||
|
public int CatalogItemId { get; set; }
|
||||||
|
|
||||||
|
public string BasketId { get; set; }
|
||||||
|
|
||||||
|
public int Quantity { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Authorize]
|
||||||
|
public class TestController : Controller
|
||||||
|
{
|
||||||
|
private readonly IHttpClientFactory _client;
|
||||||
|
private readonly IIdentityParser<ApplicationUser> _appUserParser;
|
||||||
|
|
||||||
|
public TestController(IHttpClientFactory client, IIdentityParser<ApplicationUser> identityParser)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
_appUserParser = identityParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> Ocelot()
|
||||||
|
{
|
||||||
|
var url = "http://apigw/shopping/api/v1/basket/items";
|
||||||
|
|
||||||
|
var payload = new TestPayload()
|
||||||
|
{
|
||||||
|
CatalogItemId = 1,
|
||||||
|
Quantity = 1,
|
||||||
|
BasketId = _appUserParser.Parse(User).Id
|
||||||
|
};
|
||||||
|
|
||||||
|
var content = new StringContent(JsonSerializer.Serialize(payload), System.Text.Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
|
||||||
|
var response = await _client.CreateClient(nameof(IBasketService))
|
||||||
|
.PostAsync(url, content);
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var str = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
return Ok(str);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Ok(new { response.StatusCode, response.ReasonPhrase });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
src/Web/WebUI/Dockerfile
Normal file
59
src/Web/WebUI/Dockerfile
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
# It's important to keep lines from here down to "COPY . ." identical in all Dockerfiles
|
||||||
|
# to take advantage of Docker's build cache, to speed up local container builds
|
||||||
|
COPY "eShopOnContainers-ServicesAndWebApps.sln" "eShopOnContainers-ServicesAndWebApps.sln"
|
||||||
|
|
||||||
|
COPY "ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj" "ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj"
|
||||||
|
COPY "ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj" "ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj"
|
||||||
|
COPY "BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj" "BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj"
|
||||||
|
COPY "BuildingBlocks/EventBus/EventBus/EventBus.csproj" "BuildingBlocks/EventBus/EventBus/EventBus.csproj"
|
||||||
|
COPY "BuildingBlocks/EventBus/EventBus.Tests/EventBus.Tests.csproj" "BuildingBlocks/EventBus/EventBus.Tests/EventBus.Tests.csproj"
|
||||||
|
COPY "BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj" "BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj"
|
||||||
|
COPY "BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj" "BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj"
|
||||||
|
COPY "BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj" "BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj"
|
||||||
|
COPY "BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj" "BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj"
|
||||||
|
COPY "Services/Basket/Basket.API/Basket.API.csproj" "Services/Basket/Basket.API/Basket.API.csproj"
|
||||||
|
COPY "Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj" "Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj"
|
||||||
|
COPY "Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj" "Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj"
|
||||||
|
COPY "Services/Catalog/Catalog.API/Catalog.API.csproj" "Services/Catalog/Catalog.API/Catalog.API.csproj"
|
||||||
|
COPY "Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj" "Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj"
|
||||||
|
COPY "Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj" "Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj"
|
||||||
|
COPY "Services/Identity/Identity.API/Identity.API.csproj" "Services/Identity/Identity.API/Identity.API.csproj"
|
||||||
|
COPY "Services/Ordering/Ordering.API/Ordering.API.csproj" "Services/Ordering/Ordering.API/Ordering.API.csproj"
|
||||||
|
COPY "Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj" "Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj"
|
||||||
|
COPY "Services/Ordering/Ordering.Domain/Ordering.Domain.csproj" "Services/Ordering/Ordering.Domain/Ordering.Domain.csproj"
|
||||||
|
COPY "Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj" "Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj"
|
||||||
|
COPY "Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj" "Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj"
|
||||||
|
COPY "Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj" "Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj"
|
||||||
|
COPY "Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj" "Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj"
|
||||||
|
COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment.API/Payment.API.csproj"
|
||||||
|
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
||||||
|
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
||||||
|
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
||||||
|
COPY "Web/WebUI/WebUI.csproj" "Web/WebUI/WebUI.csproj"
|
||||||
|
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
||||||
|
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
||||||
|
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
||||||
|
|
||||||
|
COPY "docker-compose.dcproj" "docker-compose.dcproj"
|
||||||
|
|
||||||
|
COPY "NuGet.config" "NuGet.config"
|
||||||
|
|
||||||
|
RUN dotnet restore "eShopOnContainers-ServicesAndWebApps.sln"
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
WORKDIR /src/Web/WebUI
|
||||||
|
RUN dotnet publish --no-restore -c Release -o /app
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app .
|
||||||
|
ENTRYPOINT ["dotnet", "WebUI.dll"]
|
28
src/Web/WebUI/Extensions/HttpClientExtensions.cs
Normal file
28
src/Web/WebUI/Extensions/HttpClientExtensions.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebMVC.Extensions;
|
||||||
|
|
||||||
|
public static class HttpClientExtensions
|
||||||
|
{
|
||||||
|
public static void SetBasicAuthentication(this HttpClient client, string userName, string password) =>
|
||||||
|
client.DefaultRequestHeaders.Authorization = new BasicAuthenticationHeaderValue(userName, password);
|
||||||
|
|
||||||
|
public static void SetToken(this HttpClient client, string scheme, string token) =>
|
||||||
|
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(scheme, token);
|
||||||
|
|
||||||
|
public static void SetBearerToken(this HttpClient client, string token) =>
|
||||||
|
client.SetToken(JwtConstants.TokenType, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BasicAuthenticationHeaderValue : AuthenticationHeaderValue
|
||||||
|
{
|
||||||
|
public BasicAuthenticationHeaderValue(string userName, string password)
|
||||||
|
: base("Basic", EncodeCredential(userName, password))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private static string EncodeCredential(string userName, string password)
|
||||||
|
{
|
||||||
|
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
|
||||||
|
string credential = String.Format("{0}:{1}", userName, password);
|
||||||
|
|
||||||
|
return Convert.ToBase64String(encoding.GetBytes(credential));
|
||||||
|
}
|
||||||
|
}
|
16
src/Web/WebUI/Extensions/SessionExtensions.cs
Normal file
16
src/Web/WebUI/Extensions/SessionExtensions.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
public static class SessionExtensions
|
||||||
|
{
|
||||||
|
public static void SetObject(this ISession session, string key, object value) =>
|
||||||
|
session.SetString(key,JsonSerializer.Serialize(value));
|
||||||
|
|
||||||
|
public static T GetObject<T>(this ISession session, string key)
|
||||||
|
{
|
||||||
|
var value = session.GetString(key);
|
||||||
|
|
||||||
|
return value == null ? default(T) :JsonSerializer.Deserialize<T>(value, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
85
src/Web/WebUI/Infrastructure/API.cs
Normal file
85
src/Web/WebUI/Infrastructure/API.cs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
namespace WebUI.Infrastructure;
|
||||||
|
|
||||||
|
public static class API
|
||||||
|
{
|
||||||
|
|
||||||
|
public static class Purchase
|
||||||
|
{
|
||||||
|
public static string AddItemToBasket(string baseUri) => $"{baseUri}/basket/items";
|
||||||
|
public static string UpdateBasketItem(string baseUri) => $"{baseUri}/basket/items";
|
||||||
|
|
||||||
|
public static string GetOrderDraft(string baseUri, string basketId) => $"{baseUri}/order/draft/{basketId}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Basket
|
||||||
|
{
|
||||||
|
public static string GetBasket(string baseUri, string basketId) => $"{baseUri}/{basketId}";
|
||||||
|
public static string UpdateBasket(string baseUri) => baseUri;
|
||||||
|
public static string CheckoutBasket(string baseUri) => $"{baseUri}/checkout";
|
||||||
|
public static string CleanBasket(string baseUri, string basketId) => $"{baseUri}/{basketId}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Order
|
||||||
|
{
|
||||||
|
public static string GetOrder(string baseUri, string orderId)
|
||||||
|
{
|
||||||
|
return $"{baseUri}/{orderId}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetAllMyOrders(string baseUri)
|
||||||
|
{
|
||||||
|
return baseUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string AddNewOrder(string baseUri)
|
||||||
|
{
|
||||||
|
return $"{baseUri}/new";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CancelOrder(string baseUri)
|
||||||
|
{
|
||||||
|
return $"{baseUri}/cancel";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ShipOrder(string baseUri)
|
||||||
|
{
|
||||||
|
return $"{baseUri}/ship";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Catalog
|
||||||
|
{
|
||||||
|
public static string GetAllCatalogItems(string baseUri, int page, int take, int? brand, int? type)
|
||||||
|
{
|
||||||
|
var filterQs = "";
|
||||||
|
|
||||||
|
if (type.HasValue)
|
||||||
|
{
|
||||||
|
var brandQs = (brand.HasValue) ? brand.Value.ToString() : string.Empty;
|
||||||
|
filterQs = $"/type/{type.Value}/brand/{brandQs}";
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (brand.HasValue)
|
||||||
|
{
|
||||||
|
var brandQs = (brand.HasValue) ? brand.Value.ToString() : string.Empty;
|
||||||
|
filterQs = $"/type/all/brand/{brandQs}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filterQs = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"{baseUri}items{filterQs}?pageIndex={page}&pageSize={take}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetAllBrands(string baseUri)
|
||||||
|
{
|
||||||
|
return $"{baseUri}catalogBrands";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetAllTypes(string baseUri)
|
||||||
|
{
|
||||||
|
return $"{baseUri}catalogTypes";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
namespace WebUI.Infrastructure;
|
||||||
|
|
||||||
|
public class HttpClientAuthorizationDelegatingHandler
|
||||||
|
: DelegatingHandler
|
||||||
|
{
|
||||||
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
|
||||||
|
public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccessor)
|
||||||
|
{
|
||||||
|
_httpContextAccessor = httpContextAccessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var authorizationHeader = _httpContextAccessor.HttpContext
|
||||||
|
.Request.Headers["Authorization"];
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(authorizationHeader))
|
||||||
|
{
|
||||||
|
request.Headers.Add("Authorization", new List<string>() { authorizationHeader });
|
||||||
|
}
|
||||||
|
|
||||||
|
var token = await GetToken();
|
||||||
|
|
||||||
|
if (token != null)
|
||||||
|
{
|
||||||
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await base.SendAsync(request, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<string> GetToken()
|
||||||
|
{
|
||||||
|
const string ACCESS_TOKEN = "access_token";
|
||||||
|
|
||||||
|
return await _httpContextAccessor.HttpContext
|
||||||
|
.GetTokenAsync(ACCESS_TOKEN);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
namespace WebUI.Infrastructure;
|
||||||
|
|
||||||
|
public class HttpClientRequestIdDelegatingHandler
|
||||||
|
: DelegatingHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
public HttpClientRequestIdDelegatingHandler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (request.Method == HttpMethod.Post || request.Method == HttpMethod.Put)
|
||||||
|
{
|
||||||
|
if (!request.Headers.Contains("x-requestid"))
|
||||||
|
{
|
||||||
|
request.Headers.Add("x-requestid", Guid.NewGuid().ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await base.SendAsync(request, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
83
src/Web/WebUI/Infrastructure/WebContextSeed.cs
Normal file
83
src/Web/WebUI/Infrastructure/WebContextSeed.cs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
namespace WebUI.Infrastructure;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
public class WebContextSeed
|
||||||
|
{
|
||||||
|
public static void Seed(IApplicationBuilder applicationBuilder, IWebHostEnvironment env)
|
||||||
|
{
|
||||||
|
var log = Serilog.Log.Logger;
|
||||||
|
|
||||||
|
var settings = (AppSettings)applicationBuilder
|
||||||
|
.ApplicationServices.GetRequiredService<IOptions<AppSettings>>().Value;
|
||||||
|
|
||||||
|
var useCustomizationData = settings.UseCustomizationData;
|
||||||
|
var contentRootPath = env.ContentRootPath;
|
||||||
|
var webroot = env.WebRootPath;
|
||||||
|
|
||||||
|
if (useCustomizationData)
|
||||||
|
{
|
||||||
|
GetPreconfiguredImages(contentRootPath, webroot, log);
|
||||||
|
|
||||||
|
GetPreconfiguredCSS(contentRootPath, webroot, log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetPreconfiguredCSS(string contentRootPath, string webroot, ILogger log)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string overrideCssFile = Path.Combine(contentRootPath, "Setup", "override.css");
|
||||||
|
if (!File.Exists(overrideCssFile))
|
||||||
|
{
|
||||||
|
log.Error("Override css file '{FileName}' does not exists.", overrideCssFile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string destinationFilename = Path.Combine(webroot, "css", "override.css");
|
||||||
|
File.Copy(overrideCssFile, destinationFilename, true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.Error(ex, "EXCEPTION ERROR: {Message}", ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetPreconfiguredImages(string contentRootPath, string webroot, ILogger log)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip");
|
||||||
|
if (!File.Exists(imagesZipFile))
|
||||||
|
{
|
||||||
|
log.Error("Zip file '{ZipFileName}' does not exists.", imagesZipFile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string imagePath = Path.Combine(webroot, "images");
|
||||||
|
string[] imageFiles = Directory.GetFiles(imagePath).Select(file => Path.GetFileName(file)).ToArray();
|
||||||
|
|
||||||
|
using ZipArchive zip = ZipFile.Open(imagesZipFile, ZipArchiveMode.Read);
|
||||||
|
foreach (ZipArchiveEntry entry in zip.Entries)
|
||||||
|
{
|
||||||
|
if (imageFiles.Contains(entry.Name))
|
||||||
|
{
|
||||||
|
string destinationFilename = Path.Combine(imagePath, entry.Name);
|
||||||
|
if (File.Exists(destinationFilename))
|
||||||
|
{
|
||||||
|
File.Delete(destinationFilename);
|
||||||
|
}
|
||||||
|
entry.ExtractToFile(destinationFilename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.Warning("Skipped file '{FileName}' in zipfile '{ZipFileName}'", entry.Name, imagesZipFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.Error(ex, "EXCEPTION ERROR: {Message}", ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
68
src/Web/WebUI/Program.cs
Normal file
68
src/Web/WebUI/Program.cs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
var configuration = GetConfiguration();
|
||||||
|
|
||||||
|
Log.Logger = CreateSerilogLogger(configuration);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log.Information("Configuring web host ({ApplicationContext})...", Program.AppName);
|
||||||
|
var host = BuildWebHost(configuration, args);
|
||||||
|
|
||||||
|
Log.Information("Starting web host ({ApplicationContext})...", Program.AppName);
|
||||||
|
host.Run();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", Program.AppName);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Log.CloseAndFlush();
|
||||||
|
}
|
||||||
|
|
||||||
|
IWebHost BuildWebHost(IConfiguration configuration, string[] args) =>
|
||||||
|
WebHost.CreateDefaultBuilder(args)
|
||||||
|
.CaptureStartupErrors(false)
|
||||||
|
.ConfigureAppConfiguration(x => x.AddConfiguration(configuration))
|
||||||
|
.UseStartup<Startup>()
|
||||||
|
.UseSerilog()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
var seqServerUrl = configuration["Serilog:SeqServerUrl"];
|
||||||
|
var logstashUrl = configuration["Serilog:LogstashgUrl"];
|
||||||
|
var cfg = new LoggerConfiguration()
|
||||||
|
.ReadFrom.Configuration(configuration)
|
||||||
|
.Enrich.WithProperty("ApplicationContext", Program.AppName)
|
||||||
|
.Enrich.FromLogContext()
|
||||||
|
.WriteTo.Console();
|
||||||
|
if (!string.IsNullOrWhiteSpace(seqServerUrl))
|
||||||
|
{
|
||||||
|
cfg.WriteTo.Seq(seqServerUrl);
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(logstashUrl))
|
||||||
|
{
|
||||||
|
cfg.WriteTo.Http(logstashUrl);
|
||||||
|
}
|
||||||
|
return cfg.CreateLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
IConfiguration GetConfiguration()
|
||||||
|
{
|
||||||
|
var builder = new ConfigurationBuilder()
|
||||||
|
.SetBasePath(Directory.GetCurrentDirectory())
|
||||||
|
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
|
||||||
|
.AddEnvironmentVariables();
|
||||||
|
|
||||||
|
return builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public partial class Program
|
||||||
|
{
|
||||||
|
private static readonly string _namespace = typeof(Startup).Namespace;
|
||||||
|
public static readonly string AppName = _namespace.Substring(_namespace.LastIndexOf('.', _namespace.LastIndexOf('.') - 1) + 1);
|
||||||
|
}
|
26
src/Web/WebUI/Properties/launchSettings.json
Normal file
26
src/Web/WebUI/Properties/launchSettings.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:5300",
|
||||||
|
"sslPort": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.eShopOnContainers.WebUI": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "http://localhost:5200",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
120
src/Web/WebUI/Services/BasketService.cs
Normal file
120
src/Web/WebUI/Services/BasketService.cs
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.Services;
|
||||||
|
|
||||||
|
using Microsoft.eShopOnContainers.WebUI.ViewModels;
|
||||||
|
|
||||||
|
public class BasketService : IBasketService
|
||||||
|
{
|
||||||
|
private readonly IOptions<AppSettings> _settings;
|
||||||
|
private readonly HttpClient _apiClient;
|
||||||
|
private readonly ILogger<BasketService> _logger;
|
||||||
|
private readonly string _basketByPassUrl;
|
||||||
|
private readonly string _purchaseUrl;
|
||||||
|
|
||||||
|
public BasketService(HttpClient httpClient, IOptions<AppSettings> settings, ILogger<BasketService> logger)
|
||||||
|
{
|
||||||
|
_apiClient = httpClient;
|
||||||
|
_settings = settings;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
_basketByPassUrl = $"{_settings.Value.PurchaseUrl}/b/api/v1/basket";
|
||||||
|
_purchaseUrl = $"{_settings.Value.PurchaseUrl}/api/v1";
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Basket> GetBasket(ApplicationUser user)
|
||||||
|
{
|
||||||
|
var uri = API.Basket.GetBasket(_basketByPassUrl, user.Id);
|
||||||
|
_logger.LogDebug("[GetBasket] -> Calling {Uri} to get the basket", uri);
|
||||||
|
var response = await _apiClient.GetAsync(uri);
|
||||||
|
_logger.LogDebug("[GetBasket] -> response code {StatusCode}", response.StatusCode);
|
||||||
|
var responseString = await response.Content.ReadAsStringAsync();
|
||||||
|
return string.IsNullOrEmpty(responseString) ?
|
||||||
|
new Basket() { BuyerId = user.Id } :
|
||||||
|
JsonSerializer.Deserialize<Basket>(responseString, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Basket> UpdateBasket(Basket basket)
|
||||||
|
{
|
||||||
|
var uri = API.Basket.UpdateBasket(_basketByPassUrl);
|
||||||
|
|
||||||
|
var basketContent = new StringContent(JsonSerializer.Serialize(basket), System.Text.Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
var response = await _apiClient.PostAsync(uri, basketContent);
|
||||||
|
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
return basket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Checkout(BasketDTO basket)
|
||||||
|
{
|
||||||
|
var uri = API.Basket.CheckoutBasket(_basketByPassUrl);
|
||||||
|
var basketContent = new StringContent(JsonSerializer.Serialize(basket), System.Text.Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
_logger.LogInformation("Uri chechout {uri}", uri);
|
||||||
|
|
||||||
|
var response = await _apiClient.PostAsync(uri, basketContent);
|
||||||
|
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Basket> SetQuantities(ApplicationUser user, Dictionary<string, int> quantities)
|
||||||
|
{
|
||||||
|
var uri = API.Purchase.UpdateBasketItem(_purchaseUrl);
|
||||||
|
|
||||||
|
var basketUpdate = new
|
||||||
|
{
|
||||||
|
BasketId = user.Id,
|
||||||
|
Updates = quantities.Select(kvp => new
|
||||||
|
{
|
||||||
|
BasketItemId = kvp.Key,
|
||||||
|
NewQty = kvp.Value
|
||||||
|
}).ToArray()
|
||||||
|
};
|
||||||
|
|
||||||
|
var basketContent = new StringContent(JsonSerializer.Serialize(basketUpdate), System.Text.Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
var response = await _apiClient.PutAsync(uri, basketContent);
|
||||||
|
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
var jsonResponse = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
return JsonSerializer.Deserialize<Basket>(jsonResponse, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Order> GetOrderDraft(string basketId)
|
||||||
|
{
|
||||||
|
var uri = API.Purchase.GetOrderDraft(_purchaseUrl, basketId);
|
||||||
|
|
||||||
|
var responseString = await _apiClient.GetStringAsync(uri);
|
||||||
|
|
||||||
|
var response = JsonSerializer.Deserialize<Order>(responseString, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AddItemToBasket(ApplicationUser user, int productId)
|
||||||
|
{
|
||||||
|
var uri = API.Purchase.AddItemToBasket(_purchaseUrl);
|
||||||
|
|
||||||
|
var newItem = new
|
||||||
|
{
|
||||||
|
CatalogItemId = productId,
|
||||||
|
BasketId = user.Id,
|
||||||
|
Quantity = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
var basketContent = new StringContent(JsonSerializer.Serialize(newItem), System.Text.Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
var response = await _apiClient.PostAsync(uri, basketContent);
|
||||||
|
}
|
||||||
|
}
|
80
src/Web/WebUI/Services/CatalogService.cs
Normal file
80
src/Web/WebUI/Services/CatalogService.cs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.Services;
|
||||||
|
|
||||||
|
public class CatalogService : ICatalogService
|
||||||
|
{
|
||||||
|
private readonly IOptions<AppSettings> _settings;
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly ILogger<CatalogService> _logger;
|
||||||
|
|
||||||
|
private readonly string _remoteServiceBaseUrl;
|
||||||
|
|
||||||
|
public CatalogService(HttpClient httpClient, ILogger<CatalogService> logger, IOptions<AppSettings> settings)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_settings = settings;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
_remoteServiceBaseUrl = $"{_settings.Value.PurchaseUrl}/c/api/v1/catalog/";
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Catalog> GetCatalogItems(int page, int take, int? brand, int? type)
|
||||||
|
{
|
||||||
|
var uri = API.Catalog.GetAllCatalogItems(_remoteServiceBaseUrl, page, take, brand, type);
|
||||||
|
|
||||||
|
var responseString = await _httpClient.GetStringAsync(uri);
|
||||||
|
|
||||||
|
var catalog = JsonSerializer.Deserialize<Catalog>(responseString, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
});
|
||||||
|
|
||||||
|
return catalog;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<SelectListItem>> GetBrands()
|
||||||
|
{
|
||||||
|
var uri = API.Catalog.GetAllBrands(_remoteServiceBaseUrl);
|
||||||
|
|
||||||
|
var responseString = await _httpClient.GetStringAsync(uri);
|
||||||
|
|
||||||
|
var items = new List<SelectListItem>();
|
||||||
|
|
||||||
|
items.Add(new SelectListItem() { Value = null, Text = "All", Selected = true });
|
||||||
|
|
||||||
|
using var brands = JsonDocument.Parse(responseString);
|
||||||
|
|
||||||
|
foreach (JsonElement brand in brands.RootElement.EnumerateArray())
|
||||||
|
{
|
||||||
|
items.Add(new SelectListItem()
|
||||||
|
{
|
||||||
|
Value = brand.GetProperty("id").ToString(),
|
||||||
|
Text = brand.GetProperty("brand").ToString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<SelectListItem>> GetTypes()
|
||||||
|
{
|
||||||
|
var uri = API.Catalog.GetAllTypes(_remoteServiceBaseUrl);
|
||||||
|
|
||||||
|
var responseString = await _httpClient.GetStringAsync(uri);
|
||||||
|
|
||||||
|
var items = new List<SelectListItem>();
|
||||||
|
items.Add(new SelectListItem() { Value = null, Text = "All", Selected = true });
|
||||||
|
|
||||||
|
using var catalogTypes = JsonDocument.Parse(responseString);
|
||||||
|
|
||||||
|
foreach (JsonElement catalogType in catalogTypes.RootElement.EnumerateArray())
|
||||||
|
{
|
||||||
|
items.Add(new SelectListItem()
|
||||||
|
{
|
||||||
|
Value = catalogType.GetProperty("id").ToString(),
|
||||||
|
Text = catalogType.GetProperty("type").ToString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
13
src/Web/WebUI/Services/IBasketService.cs
Normal file
13
src/Web/WebUI/Services/IBasketService.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.Services;
|
||||||
|
|
||||||
|
using Microsoft.eShopOnContainers.WebUI.ViewModels;
|
||||||
|
|
||||||
|
public interface IBasketService
|
||||||
|
{
|
||||||
|
Task<Basket> GetBasket(ApplicationUser user);
|
||||||
|
Task AddItemToBasket(ApplicationUser user, int productId);
|
||||||
|
Task<Basket> UpdateBasket(Basket basket);
|
||||||
|
Task Checkout(BasketDTO basket);
|
||||||
|
Task<Basket> SetQuantities(ApplicationUser user, Dictionary<string, int> quantities);
|
||||||
|
Task<Order> GetOrderDraft(string basketId);
|
||||||
|
}
|
8
src/Web/WebUI/Services/ICatalogService.cs
Normal file
8
src/Web/WebUI/Services/ICatalogService.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.Services;
|
||||||
|
|
||||||
|
public interface ICatalogService
|
||||||
|
{
|
||||||
|
Task<Catalog> GetCatalogItems(int page, int take, int? brand, int? type);
|
||||||
|
Task<IEnumerable<SelectListItem>> GetBrands();
|
||||||
|
Task<IEnumerable<SelectListItem>> GetTypes();
|
||||||
|
}
|
6
src/Web/WebUI/Services/IIdentityParser.cs
Normal file
6
src/Web/WebUI/Services/IIdentityParser.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.Services;
|
||||||
|
|
||||||
|
public interface IIdentityParser<T>
|
||||||
|
{
|
||||||
|
T Parse(IPrincipal principal);
|
||||||
|
}
|
13
src/Web/WebUI/Services/IOrderingService.cs
Normal file
13
src/Web/WebUI/Services/IOrderingService.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.Services;
|
||||||
|
using Microsoft.eShopOnContainers.WebUI.ViewModels;
|
||||||
|
|
||||||
|
public interface IOrderingService
|
||||||
|
{
|
||||||
|
Task<List<Order>> GetMyOrders(ApplicationUser user);
|
||||||
|
Task<Order> GetOrder(ApplicationUser user, string orderId);
|
||||||
|
Task CancelOrder(string orderId);
|
||||||
|
Task ShipOrder(string orderId);
|
||||||
|
Order MapUserInfoIntoOrder(ApplicationUser user, Order order);
|
||||||
|
BasketDTO MapOrderToBasket(Order order);
|
||||||
|
void OverrideUserInfoIntoOrder(Order original, Order destination);
|
||||||
|
}
|
33
src/Web/WebUI/Services/IdentityParser.cs
Normal file
33
src/Web/WebUI/Services/IdentityParser.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.Services;
|
||||||
|
|
||||||
|
public class IdentityParser : IIdentityParser<ApplicationUser>
|
||||||
|
{
|
||||||
|
public ApplicationUser Parse(IPrincipal principal)
|
||||||
|
{
|
||||||
|
// Pattern matching 'is' expression
|
||||||
|
// assigns "claims" if "principal" is a "ClaimsPrincipal"
|
||||||
|
if (principal is ClaimsPrincipal claims)
|
||||||
|
{
|
||||||
|
return new ApplicationUser
|
||||||
|
{
|
||||||
|
|
||||||
|
CardHolderName = claims.Claims.FirstOrDefault(x => x.Type == "card_holder")?.Value ?? "",
|
||||||
|
CardNumber = claims.Claims.FirstOrDefault(x => x.Type == "card_number")?.Value ?? "",
|
||||||
|
Expiration = claims.Claims.FirstOrDefault(x => x.Type == "card_expiration")?.Value ?? "",
|
||||||
|
CardType = int.Parse(claims.Claims.FirstOrDefault(x => x.Type == "missing")?.Value ?? "0"),
|
||||||
|
City = claims.Claims.FirstOrDefault(x => x.Type == "address_city")?.Value ?? "",
|
||||||
|
Country = claims.Claims.FirstOrDefault(x => x.Type == "address_country")?.Value ?? "",
|
||||||
|
Email = claims.Claims.FirstOrDefault(x => x.Type == "email")?.Value ?? "",
|
||||||
|
Id = claims.Claims.FirstOrDefault(x => x.Type == "sub")?.Value ?? "",
|
||||||
|
LastName = claims.Claims.FirstOrDefault(x => x.Type == "last_name")?.Value ?? "",
|
||||||
|
Name = claims.Claims.FirstOrDefault(x => x.Type == "name")?.Value ?? "",
|
||||||
|
PhoneNumber = claims.Claims.FirstOrDefault(x => x.Type == "phone_number")?.Value ?? "",
|
||||||
|
SecurityNumber = claims.Claims.FirstOrDefault(x => x.Type == "card_security_number")?.Value ?? "",
|
||||||
|
State = claims.Claims.FirstOrDefault(x => x.Type == "address_state")?.Value ?? "",
|
||||||
|
Street = claims.Claims.FirstOrDefault(x => x.Type == "address_street")?.Value ?? "",
|
||||||
|
ZipCode = claims.Claims.FirstOrDefault(x => x.Type == "address_zip_code")?.Value ?? ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw new ArgumentException(message: "The principal must be a ClaimsPrincipal", paramName: nameof(principal));
|
||||||
|
}
|
||||||
|
}
|
32
src/Web/WebUI/Services/ModelDTOs/BasketDTO.cs
Normal file
32
src/Web/WebUI/Services/ModelDTOs/BasketDTO.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
namespace WebUI.Services.ModelDTOs;
|
||||||
|
|
||||||
|
public record BasketDTO
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string City { get; init; }
|
||||||
|
[Required]
|
||||||
|
public string Street { get; init; }
|
||||||
|
[Required]
|
||||||
|
public string State { get; init; }
|
||||||
|
[Required]
|
||||||
|
public string Country { get; init; }
|
||||||
|
|
||||||
|
public string ZipCode { get; init; }
|
||||||
|
[Required]
|
||||||
|
public string CardNumber { get; init; }
|
||||||
|
[Required]
|
||||||
|
public string CardHolderName { get; init; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public DateTime CardExpiration { get; init; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string CardSecurityNumber { get; init; }
|
||||||
|
|
||||||
|
public int CardTypeId { get; init; }
|
||||||
|
|
||||||
|
public string Buyer { get; init; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public Guid RequestId { get; init; }
|
||||||
|
}
|
7
src/Web/WebUI/Services/ModelDTOs/LocationDTO.cs
Normal file
7
src/Web/WebUI/Services/ModelDTOs/LocationDTO.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace WebUI.Services.ModelDTOs;
|
||||||
|
|
||||||
|
public record LocationDTO
|
||||||
|
{
|
||||||
|
public double Longitude { get; init; }
|
||||||
|
public double Latitude { get; init; }
|
||||||
|
}
|
7
src/Web/WebUI/Services/ModelDTOs/OrderDTO.cs
Normal file
7
src/Web/WebUI/Services/ModelDTOs/OrderDTO.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace WebUI.Services.ModelDTOs;
|
||||||
|
|
||||||
|
public record OrderDTO
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string OrderNumber { get; init; }
|
||||||
|
}
|
19
src/Web/WebUI/Services/ModelDTOs/OrderProcessAction.cs
Normal file
19
src/Web/WebUI/Services/ModelDTOs/OrderProcessAction.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
namespace WebUI.Services.ModelDTOs;
|
||||||
|
|
||||||
|
public record OrderProcessAction
|
||||||
|
{
|
||||||
|
public string Code { get; }
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public static OrderProcessAction Ship = new OrderProcessAction(nameof(Ship).ToLowerInvariant(), "Ship");
|
||||||
|
|
||||||
|
protected OrderProcessAction()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrderProcessAction(string code, string name)
|
||||||
|
{
|
||||||
|
Code = code;
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
}
|
140
src/Web/WebUI/Services/OrderingService.cs
Normal file
140
src/Web/WebUI/Services/OrderingService.cs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.Services;
|
||||||
|
|
||||||
|
using Microsoft.eShopOnContainers.WebUI.ViewModels;
|
||||||
|
|
||||||
|
public class OrderingService : IOrderingService
|
||||||
|
{
|
||||||
|
private HttpClient _httpClient;
|
||||||
|
private readonly string _remoteServiceBaseUrl;
|
||||||
|
private readonly IOptions<AppSettings> _settings;
|
||||||
|
|
||||||
|
|
||||||
|
public OrderingService(HttpClient httpClient, IOptions<AppSettings> settings)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_settings = settings;
|
||||||
|
|
||||||
|
_remoteServiceBaseUrl = $"{settings.Value.PurchaseUrl}/o/api/v1/orders";
|
||||||
|
}
|
||||||
|
|
||||||
|
async public Task<Order> GetOrder(ApplicationUser user, string id)
|
||||||
|
{
|
||||||
|
var uri = API.Order.GetOrder(_remoteServiceBaseUrl, id);
|
||||||
|
|
||||||
|
var responseString = await _httpClient.GetStringAsync(uri);
|
||||||
|
|
||||||
|
var response = JsonSerializer.Deserialize<Order>(responseString, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
async public Task<List<Order>> GetMyOrders(ApplicationUser user)
|
||||||
|
{
|
||||||
|
var uri = API.Order.GetAllMyOrders(_remoteServiceBaseUrl);
|
||||||
|
|
||||||
|
var responseString = await _httpClient.GetStringAsync(uri);
|
||||||
|
|
||||||
|
var response = JsonSerializer.Deserialize<List<Order>>(responseString, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async public Task CancelOrder(string orderId)
|
||||||
|
{
|
||||||
|
var order = new OrderDTO()
|
||||||
|
{
|
||||||
|
OrderNumber = orderId
|
||||||
|
};
|
||||||
|
|
||||||
|
var uri = API.Order.CancelOrder(_remoteServiceBaseUrl);
|
||||||
|
var orderContent = new StringContent(JsonSerializer.Serialize(order), System.Text.Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
var response = await _httpClient.PutAsync(uri, orderContent);
|
||||||
|
|
||||||
|
if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
|
||||||
|
{
|
||||||
|
throw new Exception("Error cancelling order, try later.");
|
||||||
|
}
|
||||||
|
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
async public Task ShipOrder(string orderId)
|
||||||
|
{
|
||||||
|
var order = new OrderDTO()
|
||||||
|
{
|
||||||
|
OrderNumber = orderId
|
||||||
|
};
|
||||||
|
|
||||||
|
var uri = API.Order.ShipOrder(_remoteServiceBaseUrl);
|
||||||
|
var orderContent = new StringContent(JsonSerializer.Serialize(order), System.Text.Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
var response = await _httpClient.PutAsync(uri, orderContent);
|
||||||
|
|
||||||
|
if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
|
||||||
|
{
|
||||||
|
throw new Exception("Error in ship order process, try later.");
|
||||||
|
}
|
||||||
|
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OverrideUserInfoIntoOrder(Order original, Order destination)
|
||||||
|
{
|
||||||
|
destination.City = original.City;
|
||||||
|
destination.Street = original.Street;
|
||||||
|
destination.State = original.State;
|
||||||
|
destination.Country = original.Country;
|
||||||
|
destination.ZipCode = original.ZipCode;
|
||||||
|
|
||||||
|
destination.CardNumber = original.CardNumber;
|
||||||
|
destination.CardHolderName = original.CardHolderName;
|
||||||
|
destination.CardExpiration = original.CardExpiration;
|
||||||
|
destination.CardSecurityNumber = original.CardSecurityNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Order MapUserInfoIntoOrder(ApplicationUser user, Order order)
|
||||||
|
{
|
||||||
|
order.City = user.City;
|
||||||
|
order.Street = user.Street;
|
||||||
|
order.State = user.State;
|
||||||
|
order.Country = user.Country;
|
||||||
|
order.ZipCode = user.ZipCode;
|
||||||
|
|
||||||
|
order.CardNumber = user.CardNumber;
|
||||||
|
order.CardHolderName = user.CardHolderName;
|
||||||
|
order.CardExpiration = new DateTime(int.Parse("20" + user.Expiration.Split('/')[1]), int.Parse(user.Expiration.Split('/')[0]), 1);
|
||||||
|
order.CardSecurityNumber = user.SecurityNumber;
|
||||||
|
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BasketDTO MapOrderToBasket(Order order)
|
||||||
|
{
|
||||||
|
order.CardExpirationApiFormat();
|
||||||
|
|
||||||
|
return new BasketDTO()
|
||||||
|
{
|
||||||
|
City = order.City,
|
||||||
|
Street = order.Street,
|
||||||
|
State = order.State,
|
||||||
|
Country = order.Country,
|
||||||
|
ZipCode = order.ZipCode,
|
||||||
|
CardNumber = order.CardNumber,
|
||||||
|
CardHolderName = order.CardHolderName,
|
||||||
|
CardExpiration = order.CardExpiration,
|
||||||
|
CardSecurityNumber = order.CardSecurityNumber,
|
||||||
|
CardTypeId = 1,
|
||||||
|
Buyer = order.Buyer,
|
||||||
|
RequestId = order.RequestId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
BIN
src/Web/WebUI/Setup/images.zip
Normal file
BIN
src/Web/WebUI/Setup/images.zip
Normal file
Binary file not shown.
3
src/Web/WebUI/Setup/override.css
Normal file
3
src/Web/WebUI/Setup/override.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.esh-catalog-button {
|
||||||
|
background-color: #83D01B; /* to override the style of this button ie. to make it red, use background-color: #FF001b; */
|
||||||
|
}
|
188
src/Web/WebUI/Startup.cs
Normal file
188
src/Web/WebUI/Startup.cs
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI;
|
||||||
|
|
||||||
|
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 IoC container.
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddControllersWithViews()
|
||||||
|
.Services
|
||||||
|
.AddAppInsight(Configuration)
|
||||||
|
.AddHealthChecks(Configuration)
|
||||||
|
.AddCustomMvc(Configuration)
|
||||||
|
.AddDevspaces()
|
||||||
|
.AddHttpClientServices(Configuration);
|
||||||
|
|
||||||
|
IdentityModelEventSource.ShowPII = true; // Caution! Do NOT use in production: https://aka.ms/IdentityModel/PII
|
||||||
|
|
||||||
|
services.AddCustomAuthentication(Configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||||
|
{
|
||||||
|
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub");
|
||||||
|
if (env.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.UseExceptionHandler("/Error");
|
||||||
|
}
|
||||||
|
|
||||||
|
var pathBase = Configuration["PATH_BASE"];
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(pathBase))
|
||||||
|
{
|
||||||
|
app.UsePathBase(pathBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseStaticFiles();
|
||||||
|
app.UseSession();
|
||||||
|
|
||||||
|
WebContextSeed.Seed(app, env);
|
||||||
|
|
||||||
|
// Fix samesite issue when running eShop from docker-compose locally as by default http protocol is being used
|
||||||
|
// Refer to https://github.com/dotnet-architecture/eShopOnContainers/issues/1391
|
||||||
|
app.UseCookiePolicy(new CookiePolicyOptions { MinimumSameSitePolicy = AspNetCore.Http.SameSiteMode.Lax });
|
||||||
|
|
||||||
|
app.UseRouting();
|
||||||
|
|
||||||
|
app.UseAuthentication();
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.UseEndpoints(endpoints =>
|
||||||
|
{
|
||||||
|
endpoints.MapControllerRoute("default", "{controller=Catalog}/{action=Index}/{id?}");
|
||||||
|
endpoints.MapControllerRoute("defaultError", "{controller=Error}/{action=Error}");
|
||||||
|
endpoints.MapControllers();
|
||||||
|
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions
|
||||||
|
{
|
||||||
|
Predicate = r => r.Name.Contains("self")
|
||||||
|
});
|
||||||
|
endpoints.MapHealthChecks("/hc", new HealthCheckOptions()
|
||||||
|
{
|
||||||
|
Predicate = _ => true,
|
||||||
|
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddApplicationInsightsTelemetry(configuration);
|
||||||
|
services.AddApplicationInsightsKubernetesEnricher();
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddHealthChecks()
|
||||||
|
.AddCheck("self", () => HealthCheckResult.Healthy())
|
||||||
|
.AddUrlGroup(new Uri(configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" });
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddOptions();
|
||||||
|
services.Configure<AppSettings>(configuration);
|
||||||
|
services.AddSession();
|
||||||
|
services.AddDistributedMemoryCache();
|
||||||
|
|
||||||
|
if (configuration.GetValue<string>("IsClusterEnv") == bool.TrueString)
|
||||||
|
{
|
||||||
|
services.AddDataProtection(opts =>
|
||||||
|
{
|
||||||
|
opts.ApplicationDiscriminator = "eshop.webui";
|
||||||
|
})
|
||||||
|
.PersistKeysToStackExchangeRedis(ConnectionMultiplexer.Connect(configuration["DPConnectionString"]), "DataProtection-Keys");
|
||||||
|
}
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds all Http client services
|
||||||
|
public static IServiceCollection AddHttpClientServices(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||||
|
|
||||||
|
//register delegating handlers
|
||||||
|
services.AddTransient<HttpClientAuthorizationDelegatingHandler>();
|
||||||
|
services.AddTransient<HttpClientRequestIdDelegatingHandler>();
|
||||||
|
|
||||||
|
//set 5 min as the lifetime for each HttpMessageHandler int the pool
|
||||||
|
services.AddHttpClient("extendedhandlerlifetime").SetHandlerLifetime(TimeSpan.FromMinutes(5)).AddDevspacesSupport();
|
||||||
|
|
||||||
|
//add http client services
|
||||||
|
services.AddHttpClient<IBasketService, BasketService>()
|
||||||
|
.SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Sample. Default lifetime is 2 minutes
|
||||||
|
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
|
||||||
|
.AddDevspacesSupport();
|
||||||
|
|
||||||
|
services.AddHttpClient<ICatalogService, CatalogService>()
|
||||||
|
.AddDevspacesSupport();
|
||||||
|
|
||||||
|
services.AddHttpClient<IOrderingService, OrderingService>()
|
||||||
|
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
|
||||||
|
.AddHttpMessageHandler<HttpClientRequestIdDelegatingHandler>()
|
||||||
|
.AddDevspacesSupport();
|
||||||
|
|
||||||
|
|
||||||
|
//add custom application services
|
||||||
|
services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>();
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
var identityUrl = configuration.GetValue<string>("IdentityUrl");
|
||||||
|
var callBackUrl = configuration.GetValue<string>("CallBackUrl");
|
||||||
|
var sessionCookieLifetime = configuration.GetValue("SessionCookieLifetimeMinutes", 60);
|
||||||
|
|
||||||
|
// Add Authentication services
|
||||||
|
|
||||||
|
services.AddAuthentication(options =>
|
||||||
|
{
|
||||||
|
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||||
|
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
})
|
||||||
|
.AddCookie(setup => setup.ExpireTimeSpan = TimeSpan.FromMinutes(sessionCookieLifetime))
|
||||||
|
.AddOpenIdConnect(options =>
|
||||||
|
{
|
||||||
|
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||||
|
options.Authority = identityUrl.ToString();
|
||||||
|
options.SignedOutRedirectUri = callBackUrl.ToString();
|
||||||
|
options.ClientId = "ui";
|
||||||
|
options.ClientSecret = "secret";
|
||||||
|
options.ResponseType = "code id_token";
|
||||||
|
options.SaveTokens = true;
|
||||||
|
options.GetClaimsFromUserInfoEndpoint = true;
|
||||||
|
options.RequireHttpsMetadata = false;
|
||||||
|
options.Scope.Add("openid");
|
||||||
|
options.Scope.Add("profile");
|
||||||
|
options.Scope.Add("orders");
|
||||||
|
options.Scope.Add("basket");
|
||||||
|
options.Scope.Add("webshoppingagg");
|
||||||
|
options.Scope.Add("orders.signalrhub");
|
||||||
|
});
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
30
src/Web/WebUI/ViewComponents/Cart.cs
Normal file
30
src/Web/WebUI/ViewComponents/Cart.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.ViewComponents;
|
||||||
|
|
||||||
|
public class Cart : ViewComponent
|
||||||
|
{
|
||||||
|
private readonly IBasketService _cartSvc;
|
||||||
|
|
||||||
|
public Cart(IBasketService cartSvc) => _cartSvc = cartSvc;
|
||||||
|
|
||||||
|
public async Task<IViewComponentResult> InvokeAsync(ApplicationUser user)
|
||||||
|
{
|
||||||
|
var vm = new CartComponentViewModel();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var itemsInCart = await ItemsInCartAsync(user);
|
||||||
|
vm.ItemsCount = itemsInCart;
|
||||||
|
return View(vm);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
ViewBag.IsBasketInoperative = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return View(vm);
|
||||||
|
}
|
||||||
|
private async Task<int> ItemsInCartAsync(ApplicationUser user)
|
||||||
|
{
|
||||||
|
var basket = await _cartSvc.GetBasket(user);
|
||||||
|
return basket.Items.Count;
|
||||||
|
}
|
||||||
|
}
|
26
src/Web/WebUI/ViewComponents/CartList.cs
Normal file
26
src/Web/WebUI/ViewComponents/CartList.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.ViewComponents;
|
||||||
|
|
||||||
|
public class CartList : ViewComponent
|
||||||
|
{
|
||||||
|
private readonly IBasketService _cartSvc;
|
||||||
|
|
||||||
|
public CartList(IBasketService cartSvc) => _cartSvc = cartSvc;
|
||||||
|
|
||||||
|
public async Task<IViewComponentResult> InvokeAsync(ApplicationUser user)
|
||||||
|
{
|
||||||
|
var vm = new Basket();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
vm = await GetItemsAsync(user);
|
||||||
|
return View(vm);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ViewBag.BasketInoperativeMsg = $"Basket Service is inoperative, please try later on. ({ex.GetType().Name} - {ex.Message}))";
|
||||||
|
}
|
||||||
|
|
||||||
|
return View(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<Basket> GetItemsAsync(ApplicationUser user) => _cartSvc.GetBasket(user);
|
||||||
|
}
|
27
src/Web/WebUI/ViewModels/Annotations/CardExpiration.cs
Normal file
27
src/Web/WebUI/ViewModels/Annotations/CardExpiration.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.ViewModels.Annotations;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
|
||||||
|
public class CardExpirationAttribute : ValidationAttribute
|
||||||
|
{
|
||||||
|
public override bool IsValid(object value)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var monthString = value.ToString().Split('/')[0];
|
||||||
|
var yearString = $"20{value.ToString().Split('/')[1]}";
|
||||||
|
// Use the 'out' variable initializer to simplify
|
||||||
|
// the logic of validating the expiration date
|
||||||
|
if ((int.TryParse(monthString, out var month)) &&
|
||||||
|
(int.TryParse(yearString, out var year)))
|
||||||
|
{
|
||||||
|
DateTime d = new DateTime(year, month, 1);
|
||||||
|
|
||||||
|
return d > DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
src/Web/WebUI/ViewModels/Annotations/LatitudeCoordinate.cs
Normal file
18
src/Web/WebUI/ViewModels/Annotations/LatitudeCoordinate.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
namespace WebUI.ViewModels.Annotations;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
|
||||||
|
public class LatitudeCoordinate : ValidationAttribute
|
||||||
|
{
|
||||||
|
protected override ValidationResult
|
||||||
|
IsValid(object value, ValidationContext validationContext)
|
||||||
|
{
|
||||||
|
double coordinate;
|
||||||
|
if (!double.TryParse(value.ToString(), out coordinate) || (coordinate < -90 || coordinate > 90))
|
||||||
|
{
|
||||||
|
return new ValidationResult
|
||||||
|
("Latitude must be between -90 and 90 degrees inclusive.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ValidationResult.Success;
|
||||||
|
}
|
||||||
|
}
|
21
src/Web/WebUI/ViewModels/Annotations/LongitudeCoordinate.cs
Normal file
21
src/Web/WebUI/ViewModels/Annotations/LongitudeCoordinate.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace WebUI.ViewModels.Annotations;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
|
||||||
|
public class LongitudeCoordinate : ValidationAttribute
|
||||||
|
{
|
||||||
|
protected override ValidationResult
|
||||||
|
IsValid(object value, ValidationContext validationContext)
|
||||||
|
{
|
||||||
|
double coordinate;
|
||||||
|
if (!double.TryParse(value.ToString(), out coordinate) || (coordinate < -180 || coordinate > 180))
|
||||||
|
{
|
||||||
|
return new ValidationResult
|
||||||
|
("Longitude must be between -180 and 180 degrees inclusive.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ValidationResult.Success;
|
||||||
|
}
|
||||||
|
}
|
24
src/Web/WebUI/ViewModels/ApplicationUser.cs
Normal file
24
src/Web/WebUI/ViewModels/ApplicationUser.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.ViewModels;
|
||||||
|
|
||||||
|
// Add profile data for application users by adding properties to the ApplicationUser class
|
||||||
|
public class ApplicationUser : IdentityUser
|
||||||
|
{
|
||||||
|
public string CardNumber { get; set; }
|
||||||
|
public string SecurityNumber { get; set; }
|
||||||
|
public string Expiration { get; set; }
|
||||||
|
public string CardHolderName { get; set; }
|
||||||
|
public int CardType { get; set; }
|
||||||
|
public string Street { get; set; }
|
||||||
|
public string City { get; set; }
|
||||||
|
public string State { get; set; }
|
||||||
|
public string StateCode { get; set; }
|
||||||
|
public string Country { get; set; }
|
||||||
|
public string CountryCode { get; set; }
|
||||||
|
public string ZipCode { get; set; }
|
||||||
|
public double Latitude { get; set; }
|
||||||
|
public double Longitude { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string Name { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string LastName { get; set; }
|
||||||
|
}
|
16
src/Web/WebUI/ViewModels/Basket.cs
Normal file
16
src/Web/WebUI/ViewModels/Basket.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.ViewModels;
|
||||||
|
|
||||||
|
public record Basket
|
||||||
|
{
|
||||||
|
// Use property initializer syntax.
|
||||||
|
// While this is often more useful for read only
|
||||||
|
// auto implemented properties, it can simplify logic
|
||||||
|
// for read/write properties.
|
||||||
|
public List<BasketItem> Items { get; init; } = new List<BasketItem>();
|
||||||
|
public string BuyerId { get; init; }
|
||||||
|
|
||||||
|
public decimal Total()
|
||||||
|
{
|
||||||
|
return Math.Round(Items.Sum(x => x.UnitPrice * x.Quantity), 2);
|
||||||
|
}
|
||||||
|
}
|
12
src/Web/WebUI/ViewModels/BasketItem.cs
Normal file
12
src/Web/WebUI/ViewModels/BasketItem.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.ViewModels;
|
||||||
|
|
||||||
|
public record BasketItem
|
||||||
|
{
|
||||||
|
public string Id { get; init; }
|
||||||
|
public int ProductId { get; init; }
|
||||||
|
public string ProductName { get; init; }
|
||||||
|
public decimal UnitPrice { get; init; }
|
||||||
|
public decimal OldUnitPrice { get; init; }
|
||||||
|
public int Quantity { get; init; }
|
||||||
|
public string PictureUrl { get; init; }
|
||||||
|
}
|
9
src/Web/WebUI/ViewModels/Campaign.cs
Normal file
9
src/Web/WebUI/ViewModels/Campaign.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.ViewModels;
|
||||||
|
|
||||||
|
public record Campaign
|
||||||
|
{
|
||||||
|
public int PageIndex { get; init; }
|
||||||
|
public int PageSize { get; init; }
|
||||||
|
public int Count { get; init; }
|
||||||
|
public List<CampaignItem> Data { get; init; }
|
||||||
|
}
|
17
src/Web/WebUI/ViewModels/CampaignItem.cs
Normal file
17
src/Web/WebUI/ViewModels/CampaignItem.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.ViewModels;
|
||||||
|
|
||||||
|
public record CampaignItem
|
||||||
|
{
|
||||||
|
public int Id { get; init; }
|
||||||
|
|
||||||
|
public string Name { get; init; }
|
||||||
|
|
||||||
|
public string Description { get; init; }
|
||||||
|
|
||||||
|
public DateTime From { get; init; }
|
||||||
|
|
||||||
|
public DateTime To { get; init; }
|
||||||
|
|
||||||
|
public string PictureUri { get; init; }
|
||||||
|
public string DetailsUri { get; init; }
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.ViewModels.CartViewModels;
|
||||||
|
|
||||||
|
public class CartComponentViewModel
|
||||||
|
{
|
||||||
|
public int ItemsCount { get; set; }
|
||||||
|
public string Disabled => (ItemsCount == 0) ? "is-disabled" : "";
|
||||||
|
}
|
9
src/Web/WebUI/ViewModels/Catalog.cs
Normal file
9
src/Web/WebUI/ViewModels/Catalog.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.ViewModels;
|
||||||
|
|
||||||
|
public record Catalog
|
||||||
|
{
|
||||||
|
public int PageIndex { get; init; }
|
||||||
|
public int PageSize { get; init; }
|
||||||
|
public int Count { get; init; }
|
||||||
|
public List<CatalogItem> Data { get; init; }
|
||||||
|
}
|
14
src/Web/WebUI/ViewModels/CatalogItem.cs
Normal file
14
src/Web/WebUI/ViewModels/CatalogItem.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.ViewModels;
|
||||||
|
|
||||||
|
public record CatalogItem
|
||||||
|
{
|
||||||
|
public int Id { get; init; }
|
||||||
|
public string Name { get; init; }
|
||||||
|
public string Description { get; init; }
|
||||||
|
public decimal Price { get; init; }
|
||||||
|
public string PictureUri { get; init; }
|
||||||
|
public int CatalogBrandId { get; init; }
|
||||||
|
public string CatalogBrand { get; init; }
|
||||||
|
public int CatalogTypeId { get; init; }
|
||||||
|
public string CatalogType { get; init; }
|
||||||
|
}
|
11
src/Web/WebUI/ViewModels/CatalogViewModels/IndexViewModel.cs
Normal file
11
src/Web/WebUI/ViewModels/CatalogViewModels/IndexViewModel.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.ViewModels.CatalogViewModels;
|
||||||
|
|
||||||
|
public class IndexViewModel
|
||||||
|
{
|
||||||
|
public IEnumerable<CatalogItem> CatalogItems { get; set; }
|
||||||
|
public IEnumerable<SelectListItem> Brands { get; set; }
|
||||||
|
public IEnumerable<SelectListItem> Types { get; set; }
|
||||||
|
public int? BrandFilterApplied { get; set; }
|
||||||
|
public int? TypesFilterApplied { get; set; }
|
||||||
|
public PaginationInfo PaginationInfo { get; set; }
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.ViewModels;
|
||||||
|
|
||||||
|
public class NumberToStringConverter : JsonConverter<string>
|
||||||
|
{
|
||||||
|
public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonTokenType.Number)
|
||||||
|
{
|
||||||
|
var numberValue = reader.GetInt32();
|
||||||
|
return numberValue.ToString();
|
||||||
|
}
|
||||||
|
else if (reader.TokenType == JsonTokenType.String)
|
||||||
|
{
|
||||||
|
return reader.GetString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new JsonException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(value);
|
||||||
|
}
|
||||||
|
}
|
7
src/Web/WebUI/ViewModels/Header.cs
Normal file
7
src/Web/WebUI/ViewModels/Header.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.ViewModels;
|
||||||
|
|
||||||
|
public record Header
|
||||||
|
{
|
||||||
|
public string Controller { get; init; }
|
||||||
|
public string Text { get; init; }
|
||||||
|
}
|
91
src/Web/WebUI/ViewModels/Order.cs
Normal file
91
src/Web/WebUI/ViewModels/Order.cs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.ViewModels;
|
||||||
|
|
||||||
|
public class Order
|
||||||
|
{
|
||||||
|
[JsonConverter(typeof(NumberToStringConverter))]
|
||||||
|
public string OrderNumber { get; set; }
|
||||||
|
|
||||||
|
public DateTime Date { get; set; }
|
||||||
|
|
||||||
|
public string Status { get; set; }
|
||||||
|
|
||||||
|
public decimal Total { get; set; }
|
||||||
|
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string City { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string Street { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string State { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string Country { get; set; }
|
||||||
|
|
||||||
|
public string ZipCode { get; set; }
|
||||||
|
[Required]
|
||||||
|
[DisplayName("Card number")]
|
||||||
|
public string CardNumber { get; set; }
|
||||||
|
[Required]
|
||||||
|
[DisplayName("Cardholder name")]
|
||||||
|
public string CardHolderName { get; set; }
|
||||||
|
|
||||||
|
public DateTime CardExpiration { get; set; }
|
||||||
|
[RegularExpression(@"(0[1-9]|1[0-2])\/[0-9]{2}", ErrorMessage = "Expiration should match a valid MM/YY value")]
|
||||||
|
[CardExpiration(ErrorMessage = "The card is expired"), Required]
|
||||||
|
[DisplayName("Card expiration")]
|
||||||
|
public string CardExpirationShort { get; set; }
|
||||||
|
[Required]
|
||||||
|
[DisplayName("Card security number")]
|
||||||
|
public string CardSecurityNumber { get; set; }
|
||||||
|
|
||||||
|
public int CardTypeId { get; set; }
|
||||||
|
|
||||||
|
public string Buyer { get; set; }
|
||||||
|
|
||||||
|
public List<SelectListItem> ActionCodeSelectList =>
|
||||||
|
GetActionCodesByCurrentState();
|
||||||
|
|
||||||
|
public List<OrderItem> OrderItems { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public Guid RequestId { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public void CardExpirationShortFormat()
|
||||||
|
{
|
||||||
|
CardExpirationShort = CardExpiration.ToString("MM/yy");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CardExpirationApiFormat()
|
||||||
|
{
|
||||||
|
var month = CardExpirationShort.Split('/')[0];
|
||||||
|
var year = $"20{CardExpirationShort.Split('/')[1]}";
|
||||||
|
|
||||||
|
CardExpiration = new DateTime(int.Parse(year), int.Parse(month), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<SelectListItem> GetActionCodesByCurrentState()
|
||||||
|
{
|
||||||
|
var actions = new List<OrderProcessAction>();
|
||||||
|
switch (Status?.ToLower())
|
||||||
|
{
|
||||||
|
case "paid":
|
||||||
|
actions.Add(OrderProcessAction.Ship);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = new List<SelectListItem>();
|
||||||
|
actions.ForEach(action =>
|
||||||
|
{
|
||||||
|
result.Add(new SelectListItem { Text = action.Name, Value = action.Code });
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum CardType
|
||||||
|
{
|
||||||
|
AMEX = 1
|
||||||
|
}
|
16
src/Web/WebUI/ViewModels/OrderItem.cs
Normal file
16
src/Web/WebUI/ViewModels/OrderItem.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.ViewModels;
|
||||||
|
|
||||||
|
public record OrderItem
|
||||||
|
{
|
||||||
|
public int ProductId { get; init; }
|
||||||
|
|
||||||
|
public string ProductName { get; init; }
|
||||||
|
|
||||||
|
public decimal UnitPrice { get; init; }
|
||||||
|
|
||||||
|
public decimal Discount { get; init; }
|
||||||
|
|
||||||
|
public int Units { get; init; }
|
||||||
|
|
||||||
|
public string PictureUrl { get; init; }
|
||||||
|
}
|
11
src/Web/WebUI/ViewModels/Pagination/PaginationInfo.cs
Normal file
11
src/Web/WebUI/ViewModels/Pagination/PaginationInfo.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.WebUI.ViewModels.Pagination;
|
||||||
|
|
||||||
|
public class PaginationInfo
|
||||||
|
{
|
||||||
|
public int TotalItems { get; set; }
|
||||||
|
public int ItemsPerPage { get; set; }
|
||||||
|
public int ActualPage { get; set; }
|
||||||
|
public int TotalPages { get; set; }
|
||||||
|
public string Previous { get; set; }
|
||||||
|
public string Next { get; set; }
|
||||||
|
}
|
19
src/Web/WebUI/Views/Cart/Index.cshtml
Normal file
19
src/Web/WebUI/Views/Cart/Index.cshtml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
@using Microsoft.eShopOnContainers.WebUI.Services
|
||||||
|
@using Microsoft.eShopOnContainers.WebUI.ViewModels
|
||||||
|
|
||||||
|
@model Microsoft.eShopOnContainers.WebUI.ViewModels.Basket
|
||||||
|
@inject IIdentityParser<ApplicationUser> UserManager
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "My Cart";
|
||||||
|
var headerList = new List<Header>() {
|
||||||
|
new Header() { Controller = "Catalog", Text = "Back to catalog" }};
|
||||||
|
}
|
||||||
|
|
||||||
|
<form method="post" id="cartForm">
|
||||||
|
<div class="esh-basket">
|
||||||
|
<partial name="_Header" model="headerList"/>
|
||||||
|
|
||||||
|
@await Component.InvokeAsync("CartList", new { user = UserManager.Parse(User) })
|
||||||
|
</div>
|
||||||
|
</form>
|
58
src/Web/WebUI/Views/Catalog/Index.cshtml
Normal file
58
src/Web/WebUI/Views/Catalog/Index.cshtml
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
@model Microsoft.eShopOnContainers.WebUI.ViewModels.CatalogViewModels.IndexViewModel
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Catalog";
|
||||||
|
}
|
||||||
|
<section class="esh-catalog-hero">
|
||||||
|
<div class="container">
|
||||||
|
<img class="esh-catalog-title" src="~/images/main_banner_text.png" />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="esh-catalog-filters">
|
||||||
|
<div class="container">
|
||||||
|
<form asp-action="Index" asp-controller="Catalog" method="post">
|
||||||
|
<label class="esh-catalog-label" data-title="brand">
|
||||||
|
<select asp-for="@Model.BrandFilterApplied" asp-items="@Model.Brands" class="esh-catalog-filter"></select>
|
||||||
|
</label>
|
||||||
|
<label class="esh-catalog-label" data-title="type">
|
||||||
|
<select asp-for="@Model.TypesFilterApplied" asp-items="@Model.Types" class="esh-catalog-filter"></select>
|
||||||
|
</label>
|
||||||
|
<input class="esh-catalog-send" type="image" src="~/images/arrow-right.svg" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<br />
|
||||||
|
@if(ViewBag.BasketInoperativeMsg != null)
|
||||||
|
{
|
||||||
|
<div class="alert alert-warning" role="alert">
|
||||||
|
@ViewBag.BasketInoperativeMsg
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (Model.CatalogItems.Count() > 0)
|
||||||
|
{
|
||||||
|
<partial name="_pagination" for="PaginationInfo" />
|
||||||
|
|
||||||
|
<div class="esh-catalog-items row">
|
||||||
|
@foreach (var catalogItem in Model.CatalogItems)
|
||||||
|
{
|
||||||
|
<div class="esh-catalog-item col-md-4">
|
||||||
|
<partial name="_product" model="catalogItem" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<partial name="_pagination" for="PaginationInfo" />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="esh-catalog-items row">
|
||||||
|
THERE ARE NO RESULTS THAT MATCH YOUR SEARCH
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
32
src/Web/WebUI/Views/Catalog/_pagination.cshtml
Normal file
32
src/Web/WebUI/Views/Catalog/_pagination.cshtml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
@model Microsoft.eShopOnContainers.WebUI.ViewModels.Pagination.PaginationInfo
|
||||||
|
|
||||||
|
<div class="esh-pager">
|
||||||
|
<div class="container">
|
||||||
|
<article class="esh-pager-wrapper row">
|
||||||
|
<nav>
|
||||||
|
<a class="esh-pager-item esh-pager-item--navigable @Model.Previous"
|
||||||
|
id="Previous"
|
||||||
|
asp-controller="Catalog"
|
||||||
|
asp-action="Index"
|
||||||
|
asp-route-page="@(Model.ActualPage -1)"
|
||||||
|
aria-label="Previous">
|
||||||
|
Previous
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<span class="esh-pager-item">
|
||||||
|
Showing @Model.ItemsPerPage of @Model.TotalItems products - Page @(Model.ActualPage + 1) - @Model.TotalPages
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<a class="esh-pager-item esh-pager-item--navigable @Model.Next"
|
||||||
|
id="Next"
|
||||||
|
asp-controller="Catalog"
|
||||||
|
asp-action="Index"
|
||||||
|
asp-route-page="@(Model.ActualPage + 1)"
|
||||||
|
aria-label="Next">
|
||||||
|
Next
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
16
src/Web/WebUI/Views/Catalog/_product.cshtml
Normal file
16
src/Web/WebUI/Views/Catalog/_product.cshtml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
@model CatalogItem
|
||||||
|
|
||||||
|
|
||||||
|
<form asp-controller="Cart" asp-action="AddToCart">
|
||||||
|
|
||||||
|
<img class="esh-catalog-thumbnail" src="@Model.PictureUri" />
|
||||||
|
<input class="esh-catalog-button @((!User.Identity.IsAuthenticated) ? "is-disabled" : "")" type="submit" value="[ ADD TO CART ]" />
|
||||||
|
|
||||||
|
<div class="esh-catalog-name">
|
||||||
|
<span>@Model.Name</span>
|
||||||
|
</div>
|
||||||
|
<div class="esh-catalog-price">
|
||||||
|
<span>@Model.Price.ToString("N2")</span>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" asp-for="@Model.Id" name="id" />
|
||||||
|
</form>
|
106
src/Web/WebUI/Views/Order/Create.cshtml
Normal file
106
src/Web/WebUI/Views/Order/Create.cshtml
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
@using Microsoft.eShopOnContainers.WebUI.Services
|
||||||
|
@model Microsoft.eShopOnContainers.WebUI.ViewModels.Order
|
||||||
|
@inject IIdentityParser<ApplicationUser> UserManager
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "New Order";
|
||||||
|
var headerList= new List<Header>() {
|
||||||
|
new Header() { Controller = "Cart", Text = "Back to cart" } };
|
||||||
|
}
|
||||||
|
|
||||||
|
<partial name="_Header" model="headerList"/>
|
||||||
|
<div class="container">
|
||||||
|
<form method="post" asp-controller="Order" asp-action="Checkout">
|
||||||
|
<section class="esh-orders_new-section">
|
||||||
|
<div class="row">
|
||||||
|
@foreach (var error in ViewData.ModelState.Values.SelectMany(err => err.Errors)) {
|
||||||
|
<div class="alert alert-warning" role="alert">
|
||||||
|
@error.ErrorMessage
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<h4 class="esh-orders_new-title">Shipping address</h4>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Street" class="esh-orders_new-title">Address</label>
|
||||||
|
<input asp-for="Street" class="form-control form-input" type="text" placeholder="Street"/>
|
||||||
|
<span asp-validation-for="Street" class="alert alert-danger" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="City" class="esh-orders_new-title">City</label>
|
||||||
|
<input asp-for="City" class="form-control form-input" type="text" placeholder="City"/>
|
||||||
|
<span asp-validation-for="City" class="alert alert-danger" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="State" class="esh-orders_new-title">State</label>
|
||||||
|
<input asp-for="State" class="form-control form-input" type="text" placeholder="State"/>
|
||||||
|
<span asp-validation-for="State" class="alert alert-danger" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Country" class="esh-orders_new-title">Country</label>
|
||||||
|
<input asp-for="Country" class="form-control form-input" type="text" placeholder="Country"/>
|
||||||
|
<span asp-validation-for="Country" class="alert alert-danger" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="esh-orders_new-section">
|
||||||
|
<h4 class="esh-orders_new-title">Payment method</h4>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="CardNumber" class="esh-orders_new-title">Card number</label>
|
||||||
|
<input asp-for="CardNumber" class="form-control form-input" type="text" placeholder="000000000000000"/>
|
||||||
|
<span asp-validation-for="CardNumber" class="alert alert-danger" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="CardHolderName" class="esh-orders_new-title">Cardholder name</label>
|
||||||
|
<input asp-for="CardHolderName" class="form-control form-input" type="text" placeholder="Cardholder"/>
|
||||||
|
<span asp-validation-for="CardHolderName" class="alert alert-danger" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="CardExpirationShort" class="esh-orders_new-title">Expiration date</label>
|
||||||
|
<input asp-for="CardExpirationShort" class="form-control form-input form-input-medium" type="text" placeholder="MM/YY"/>
|
||||||
|
<span asp-validation-for="CardExpirationShort" class="alert alert-danger" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="CardSecurityNumber" class="esh-orders_new-title">Security code</label>
|
||||||
|
<input asp-for="CardSecurityNumber" class="form-control form-input form-input-small" type="text" placeholder="000"/>
|
||||||
|
<span asp-validation-for="CardSecurityNumber" class="alert alert-danger" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
@await Html.PartialAsync("_OrderItems")
|
||||||
|
|
||||||
|
<section class="esh-orders_new-section">
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-md-9">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<input type="submit" value="[ Place Order ]" name="action" class="btn esh-orders_new-placeOrder" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<input asp-for="ZipCode" type="hidden" />
|
||||||
|
<input asp-for="RequestId" type="hidden" value="@Guid.NewGuid().ToString()"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
|
||||||
|
}
|
91
src/Web/WebUI/Views/Order/Detail.cshtml
Normal file
91
src/Web/WebUI/Views/Order/Detail.cshtml
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
@using Microsoft.eShopOnContainers.WebUI.ViewModels
|
||||||
|
|
||||||
|
@model Microsoft.eShopOnContainers.WebUI.ViewModels.Order
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Order Detail";
|
||||||
|
var headerList= new List<Header>() {
|
||||||
|
new Header() { Controller = "Catalog", Text = "Back to catalog" } };
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="esh-orders_detail">
|
||||||
|
<partial name="_Header" model="headerList"/>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<section class="esh-orders_detail-section">
|
||||||
|
<article class="esh-orders_detail-titles row">
|
||||||
|
<section class="esh-orders_detail-title col-3">Order number</section>
|
||||||
|
<section class="esh-orders_detail-title col-3">Date</section>
|
||||||
|
<section class="esh-orders_detail-title col-3">Total</section>
|
||||||
|
<section class="esh-orders_detail-title col-3">Status</section>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="esh-orders_detail-items row">
|
||||||
|
<section class="esh-orders_detail-item col-3">@Model.OrderNumber</section>
|
||||||
|
<section class="esh-orders_detail-item col-3">@Model.Date</section>
|
||||||
|
<section class="esh-orders_detail-item col-3">$@Model.Total</section>
|
||||||
|
<section class="esh-orders_detail-title col-3">@Model.Status</section>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="esh-orders_detail-section">
|
||||||
|
<article class="esh-orders_detail-titles row">
|
||||||
|
<section class="esh-orders_detail-title col-12">Description</section>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="esh-orders_detail-items row">
|
||||||
|
<section class="esh-orders_detail-item col-12">@Model.Description</section>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="esh-orders_detail-section">
|
||||||
|
<article class="esh-orders_detail-titles row">
|
||||||
|
<section class="esh-orders_detail-title col-12">Shipping address</section>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="esh-orders_detail-items row">
|
||||||
|
<section class="esh-orders_detail-item col-12">@Model.Street</section>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="esh-orders_detail-items row">
|
||||||
|
<section class="esh-orders_detail-item col-12">@Model.City</section>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="esh-orders_detail-items row">
|
||||||
|
<section class="esh-orders_detail-item col-12">@Model.Country</section>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="esh-orders_detail-section">
|
||||||
|
<article class="esh-orders_detail-titles row">
|
||||||
|
<section class="esh-orders_detail-title col-12">ORDER DETAILS</section>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
@for (int i = 0; i < Model.OrderItems.Count; i++)
|
||||||
|
{
|
||||||
|
var item = Model.OrderItems[i];
|
||||||
|
<article class="esh-orders_detail-items esh-orders_detail-items--border row">
|
||||||
|
<section class="esh-orders_detail-item col-md-4 hidden-md-down">
|
||||||
|
<img class="esh-orders_detail-image" src="@item.PictureUrl">
|
||||||
|
</section>
|
||||||
|
<section class="esh-orders_detail-item esh-orders_detail-item--middle col-4">@item.ProductName</section>
|
||||||
|
<section class="esh-orders_detail-item esh-orders_detail-item--middle col-1">$ @item.UnitPrice.ToString("N2")</section>
|
||||||
|
<section class="esh-orders_detail-item esh-orders_detail-item--middle col-1">@item.Units</section>
|
||||||
|
<section class="esh-orders_detail-item esh-orders_detail-item--middle col-2">$ @Math.Round(item.Units * item.UnitPrice, 2).ToString("N2")</section>
|
||||||
|
</article>
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="esh-orders_detail-section esh-orders_detail-section--right">
|
||||||
|
<article class="esh-orders_detail-titles esh-basket-titles--clean row">
|
||||||
|
<section class="esh-orders_detail-title col-9"></section>
|
||||||
|
<section class="esh-orders_detail-title col-2">TOTAL</section>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="esh-orders_detail-items row">
|
||||||
|
<section class="esh-orders_detail-item col-9"></section>
|
||||||
|
<section class="esh-orders_detail-item esh-orders_detail-item--mark col-2">$ @Model.Total.ToString("N2")</section>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
50
src/Web/WebUI/Views/Order/Index.cshtml
Normal file
50
src/Web/WebUI/Views/Order/Index.cshtml
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
@using Microsoft.eShopOnContainers.WebUI.ViewModels
|
||||||
|
|
||||||
|
@model IEnumerable<Microsoft.eShopOnContainers.WebUI.ViewModels.Order>
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "My Orders";
|
||||||
|
var headerList= new List<Header>() {
|
||||||
|
new Header() { Controller = "Catalog", Text = "Back to catalog" },
|
||||||
|
new Header() { Text = " / " },
|
||||||
|
new Header() { Controller = "OrderManagement", Text = "Orders Management" } };
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="esh-orders">
|
||||||
|
<partial name="_Header" model="headerList"/>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<article class="esh-orders-titles row">
|
||||||
|
<section class="esh-orders-title col-2">Order number</section>
|
||||||
|
<section class="esh-orders-title col-4">Date</section>
|
||||||
|
<section class="esh-orders-title col-2">Total</section>
|
||||||
|
<section class="esh-orders-title col-2">Status</section>
|
||||||
|
<section class="esh-orders-title col-2"></section>
|
||||||
|
</article>
|
||||||
|
@if (Model != null && Model.Any())
|
||||||
|
{
|
||||||
|
foreach (var item in Model)
|
||||||
|
{
|
||||||
|
<article class="esh-orders-items row">
|
||||||
|
<section class="esh-orders-item col-2">@Html.DisplayFor(modelItem => item.OrderNumber)</section>
|
||||||
|
<section class="esh-orders-item col-4">@Html.DisplayFor(modelItem => item.Date)</section>
|
||||||
|
<section class="esh-orders-item col-2">$ @Html.DisplayFor(modelItem => item.Total)</section>
|
||||||
|
<section class="esh-orders-item col-2">@Html.DisplayFor(modelItem => item.Status)</section>
|
||||||
|
<section class="esh-orders-item col-1">
|
||||||
|
<a class="esh-orders-link" asp-controller="Order" asp-action="Detail" asp-route-orderId="@item.OrderNumber">Detail</a>
|
||||||
|
</section>
|
||||||
|
<section class="esh-orders-item col-1">
|
||||||
|
@if (item.Status.ToLower() == "submitted")
|
||||||
|
{
|
||||||
|
<a class="esh-orders-link" asp-controller="Order" asp-action="cancel" asp-route-orderId="@item.OrderNumber">Cancel</a>
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
48
src/Web/WebUI/Views/Order/_OrderItems.cshtml
Normal file
48
src/Web/WebUI/Views/Order/_OrderItems.cshtml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
@model Microsoft.eShopOnContainers.WebUI.ViewModels.Order
|
||||||
|
|
||||||
|
|
||||||
|
<section class="esh-orders_new-section">
|
||||||
|
<article class="esh-orders_new-titles row">
|
||||||
|
<section class="esh-orders_new-title col-12">Order details</section>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
@for (int i = 0; i < Model.OrderItems.Count; i++)
|
||||||
|
{
|
||||||
|
var item = Model.OrderItems[i];
|
||||||
|
|
||||||
|
<article class="esh-orders_new-items esh-orders_new-items--border row">
|
||||||
|
<section class="esh-orders_new-item col-md-4 hidden-md-down">
|
||||||
|
<img class="esh-orders_new-image" src="@item.PictureUrl">
|
||||||
|
<input type="hidden" value="@item.PictureUrl" name=@("orderitems[" + i + "].PictureUrl") />
|
||||||
|
</section>
|
||||||
|
<section class="esh-orders_new-item esh-orders_new-item--middle col-4">
|
||||||
|
@item.ProductName
|
||||||
|
<input type="hidden" value="@item.ProductName" name=@("orderitems[" + i + "].ProductName") />
|
||||||
|
</section>
|
||||||
|
<section class="esh-orders_new-item esh-orders_new-item--middle col-1">
|
||||||
|
$ @item.UnitPrice.ToString("N2")
|
||||||
|
<input type="hidden" value="@item.UnitPrice" name=@("orderitems[" + i + "].UnitPrice") />
|
||||||
|
</section>
|
||||||
|
<section class="esh-orders_new-item esh-orders_new-item--middle col-1">
|
||||||
|
@item.Units
|
||||||
|
<input type="hidden" value="@item.Units" name=@("orderitems[" + i + "].Units") />
|
||||||
|
</section>
|
||||||
|
<section class="esh-orders_new-item esh-orders_new-item--middle col-2">$ @Math.Round(item.Units * item.UnitPrice, 2).ToString("N2")</section>
|
||||||
|
</article>
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="esh-orders_new-section esh-orders_new-section--right">
|
||||||
|
<article class="esh-orders_new-titles row">
|
||||||
|
<section class="esh-orders_new-title col-9"></section>
|
||||||
|
<section class="esh-orders_new-title col-2">Total</section>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="esh-orders_new-items row">
|
||||||
|
<section class="esh-orders_new-item col-9"></section>
|
||||||
|
<section class="esh-orders_new-item esh-orders_new-item--mark col-2">
|
||||||
|
$ @Model.Total.ToString("N2")
|
||||||
|
<input type="hidden" value="@Model.Total" name="Total"/>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
</section>
|
44
src/Web/WebUI/Views/OrderManagement/Index.cshtml
Normal file
44
src/Web/WebUI/Views/OrderManagement/Index.cshtml
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
@using Microsoft.eShopOnContainers.WebUI.ViewModels
|
||||||
|
|
||||||
|
@model IEnumerable<Microsoft.eShopOnContainers.WebUI.ViewModels.Order>
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "My Orders";
|
||||||
|
var headerList = new List<Header>() {
|
||||||
|
new Header() { Controller = "Catalog", Text = "Back to catalog" } };
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="esh-orders">
|
||||||
|
<partial name="_Header" model="headerList"/>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<article class="esh-orders-titles row">
|
||||||
|
<section class="esh-orders-title col-2">Order number</section>
|
||||||
|
<section class="esh-orders-title col-4">Date</section>
|
||||||
|
<section class="esh-orders-title col-2">Total</section>
|
||||||
|
<section class="esh-orders-title col-2">Status</section>
|
||||||
|
<section class="esh-orders-title col-2"></section>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
@foreach (var item in Model)
|
||||||
|
{
|
||||||
|
<article class="esh-orders-items row">
|
||||||
|
<section class="esh-orders-item col-2">@Html.DisplayFor(modelItem => item.OrderNumber)</section>
|
||||||
|
<section class="esh-orders-item col-4">@Html.DisplayFor(modelItem => item.Date)</section>
|
||||||
|
<section class="esh-orders-item col-2">$ @Html.DisplayFor(modelItem => item.Total)</section>
|
||||||
|
<section class="esh-orders-item col-2">@Html.DisplayFor(modelItem => item.Status)</section>
|
||||||
|
<section class="esh-orders-item col-2">
|
||||||
|
<form asp-action="OrderProcess" id="orderForm+@item.OrderNumber" method="post">
|
||||||
|
<input type="hidden" name="orderId" value="@item.OrderNumber" />
|
||||||
|
<select name="actionCode" asp-items="@item.ActionCodeSelectList"
|
||||||
|
disabled=@(item.Status != "paid")
|
||||||
|
onchange="document.getElementById('orderForm+@item.OrderNumber').submit()">
|
||||||
|
<option value=""> Select Action</option>
|
||||||
|
<option value="">------------------</option>
|
||||||
|
</select>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
33
src/Web/WebUI/Views/Shared/Components/Cart/Default.cshtml
Normal file
33
src/Web/WebUI/Views/Shared/Components/Cart/Default.cshtml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
@model Microsoft.eShopOnContainers.WebUI.ViewModels.CartViewModels.CartComponentViewModel
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "My Cart";
|
||||||
|
}
|
||||||
|
|
||||||
|
<a class="esh-basketstatus @Model.Disabled"
|
||||||
|
asp-area=""
|
||||||
|
asp-controller="Cart"
|
||||||
|
asp-action="Index">
|
||||||
|
@if (ViewBag.IsBasketInoperative == true)
|
||||||
|
{
|
||||||
|
<div class="esh-basketstatus-image">
|
||||||
|
<img src="~/images/cart-inoperative.png" />
|
||||||
|
</div>
|
||||||
|
<div class="esh-basketstatus-badge-inoperative">
|
||||||
|
X
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="esh-basketstatus-image">
|
||||||
|
<img src="~/images/cart.png" />
|
||||||
|
</div>
|
||||||
|
<div class="esh-basketstatus-badge">
|
||||||
|
@Model.ItemsCount
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,86 @@
|
|||||||
|
@model Microsoft.eShopOnContainers.WebUI.ViewModels.Basket
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "My Cart";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
@if (ViewBag.BasketInoperativeMsg != null)
|
||||||
|
{
|
||||||
|
<br />
|
||||||
|
<div class="alert alert-warning" role="alert">
|
||||||
|
@ViewBag.BasketInoperativeMsg
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<article class="esh-basket-titles row">
|
||||||
|
<br />
|
||||||
|
@if (ViewBag.BasketInoperativeMsg != null)
|
||||||
|
{
|
||||||
|
<div class="alert alert-warning" role="alert">
|
||||||
|
@ViewBag.BasketInoperativeMsg
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<section class="esh-basket-title col-3">Product</section>
|
||||||
|
<section class="esh-basket-title col-3 hidden-lg-down"></section>
|
||||||
|
<section class="esh-basket-title col-2">Price</section>
|
||||||
|
<section class="esh-basket-title col-2">Quantity</section>
|
||||||
|
<section class="esh-basket-title col-2">Cost</section>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
@for (int i = 0; i < Model.Items.Count; i++)
|
||||||
|
{
|
||||||
|
var item = Model.Items[i];
|
||||||
|
|
||||||
|
<article class="esh-basket-items row">
|
||||||
|
<section class="esh-basket-item esh-basket-item--middle col-lg-3 hidden-lg-down">
|
||||||
|
<img class="esh-basket-image" src="@item.PictureUrl" />
|
||||||
|
</section>
|
||||||
|
<section class="esh-basket-item esh-basket-item--middle col-3">@item.ProductName</section>
|
||||||
|
<section class="esh-basket-item esh-basket-item--middle col-2">$ @item.UnitPrice.ToString("N2")</section>
|
||||||
|
<section class="esh-basket-item esh-basket-item--middle col-2">
|
||||||
|
<input type="hidden" name="@("quantities[" + i +"].Key")" value="@item.Id" />
|
||||||
|
<input type="number" class="esh-basket-input" min="1" name="@("quantities[" + i +"].Value")" value="@item.Quantity" />
|
||||||
|
</section>
|
||||||
|
<section class="esh-basket-item esh-basket-item--middle esh-basket-item--mark col-2">$ @Math.Round(item.Quantity * item.UnitPrice, 2).ToString("N2")</section>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<div class="esh-basket-items--border row">
|
||||||
|
@if (item.OldUnitPrice != 0)
|
||||||
|
{
|
||||||
|
<div class="alert alert-warning esh-basket-margin12" role="alert"> Note that the price of this article changed in our Catalog. The old price when you originally added it to the basket was $ @item.OldUnitPrice </div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<article class="esh-basket-titles esh-basket-titles--clean row">
|
||||||
|
<section class="esh-basket-title col-10"></section>
|
||||||
|
<section class="esh-basket-title col-2">Total</section>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="esh-basket-items row">
|
||||||
|
<section class="esh-basket-item col-10"></section>
|
||||||
|
<section class="esh-basket-item esh-basket-item--mark col-2">$ @Model.Total().ToString("N2")</section>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="esh-basket-items row">
|
||||||
|
<section class="esh-basket-item col-7"></section>
|
||||||
|
<section class="esh-basket-item col-2">
|
||||||
|
<button class="btn esh-basket-checkout" name="name" value="" type="submit">[ Update ]</button>
|
||||||
|
</section>
|
||||||
|
<section class="esh-basket-item col-3">
|
||||||
|
<input type="submit"
|
||||||
|
class="btn esh-basket-checkout"
|
||||||
|
value="[ Checkout ]" name="action" />
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
16
src/Web/WebUI/Views/Shared/Error.cshtml
Normal file
16
src/Web/WebUI/Views/Shared/Error.cshtml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
@{
|
||||||
|
ViewData["Title"] = "Error";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="text-danger">Error.</h1>
|
||||||
|
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||||
|
|
||||||
|
<h3>Development Mode</h3>
|
||||||
|
<p>
|
||||||
|
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Development environment should not be enabled in deployed applications</strong>, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>, and restarting the application.
|
||||||
|
</p>
|
||||||
|
</div>
|
11
src/Web/WebUI/Views/Shared/_Header.cshtml
Normal file
11
src/Web/WebUI/Views/Shared/_Header.cshtml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
@model IEnumerable<Microsoft.eShopOnContainers.WebUI.ViewModels.Header>
|
||||||
|
|
||||||
|
<div class="esh-header">
|
||||||
|
<div class="container">
|
||||||
|
@foreach (var header in @Model)
|
||||||
|
{
|
||||||
|
<a class="esh-header-back" asp-area="" asp-controller="@header.Controller" asp-action="Index">@header.Text</a>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
138
src/Web/WebUI/Views/Shared/_Layout.cshtml
Normal file
138
src/Web/WebUI/Views/Shared/_Layout.cshtml
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>@ViewData["Title"] - Microsoft.eShopOnContainers.WebMVC</title>
|
||||||
|
|
||||||
|
<environment names="Development">
|
||||||
|
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
|
||||||
|
<link rel="stylesheet" href="~/css/app.css" />
|
||||||
|
<link rel="stylesheet" href="~/css/app.component.css" />
|
||||||
|
<link rel="stylesheet" href="~/css/shared/components/header/header.css" />
|
||||||
|
<link rel="stylesheet" href="~/css/shared/components/identity/identity.css" />
|
||||||
|
<link rel="stylesheet" href="~/css/shared/components/pager/pager.css" />
|
||||||
|
<link rel="stylesheet" href="~/css/basket/basket.component.css" />
|
||||||
|
<link rel="stylesheet" href="~/css/basket/basket-status/basket-status.component.css" />
|
||||||
|
<link rel="stylesheet" href="~/css/catalog/catalog.component.css" />
|
||||||
|
<link rel="stylesheet" href="~/css/orders/orders.component.css" />
|
||||||
|
<link rel="stylesheet" href="~/css/orders/orders-detail/orders-detail.component.css" />
|
||||||
|
<link rel="stylesheet" href="~/css/orders/orders-new/orders-new.component.css" />
|
||||||
|
<link rel="stylesheet" href="~/css/override.css" type="text/css" />
|
||||||
|
<link rel="stylesheet" href="~/css/site.min.css" type="text/css" />
|
||||||
|
|
||||||
|
</environment>
|
||||||
|
<environment names="Staging,Production">
|
||||||
|
<link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
|
||||||
|
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
|
||||||
|
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
|
||||||
|
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
|
||||||
|
<link rel="stylesheet" href="~/css/override.css" type="text/css" />
|
||||||
|
</environment>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="esh-app-header">
|
||||||
|
<div class="container">
|
||||||
|
<article class="row">
|
||||||
|
|
||||||
|
<section class="col-lg-7 col-md-6 col-12">
|
||||||
|
<a class="navbar-brand" routerLink="catalog">
|
||||||
|
<a asp-area="" asp-controller="Catalog" asp-action="Index">
|
||||||
|
<img src="~/images/brand.png" />
|
||||||
|
</a>
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
@await Html.PartialAsync("_LoginPartial")
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
@RenderBody()
|
||||||
|
|
||||||
|
|
||||||
|
<footer class="esh-app-footer">
|
||||||
|
<div class="container">
|
||||||
|
<article class="row">
|
||||||
|
|
||||||
|
<section class="col-sm-6">
|
||||||
|
<img class="esh-app-footer-brand" src="~/images/brand_dark.png" />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="col-sm-6">
|
||||||
|
<img class="esh-app-footer-text hidden-xs" src="~/images/main_footer_text.png" width="335" height="26" alt="footer text image" />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<environment names="Development">
|
||||||
|
<script src="~/lib/jquery/jquery.js"></script>
|
||||||
|
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
|
||||||
|
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||||
|
</environment>
|
||||||
|
<environment names="Staging,Production">
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"
|
||||||
|
asp-fallback-src="~/lib/jquery/jquery.min.js"
|
||||||
|
asp-fallback-test="window.jQuery">
|
||||||
|
</script>
|
||||||
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"
|
||||||
|
asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
|
||||||
|
asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal">
|
||||||
|
</script>
|
||||||
|
<script src="~/js/site.min.js" asp-append-version="true"></script>
|
||||||
|
</environment>
|
||||||
|
|
||||||
|
@RenderSection("scripts", required: false)
|
||||||
|
|
||||||
|
|
||||||
|
@using Microsoft.AspNetCore.Authentication;
|
||||||
|
@using Microsoft.Extensions.Options
|
||||||
|
@inject IOptions<AppSettings> settings
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
if ('@User.Identity.IsAuthenticated' === 'True') {
|
||||||
|
var timerId;
|
||||||
|
|
||||||
|
stablishConnection((conn) => registerNotificationHandlers(conn));
|
||||||
|
}
|
||||||
|
|
||||||
|
function stablishConnection(cb) {
|
||||||
|
let connection = new signalR.HubConnectionBuilder()
|
||||||
|
.withUrl('@settings.Value.SignalrHubUrl/hub/notificationhub', {
|
||||||
|
accessTokenFactory: () => {
|
||||||
|
return "Authorization", getToken();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.withAutomaticReconnect()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
connection.start().then(function () {
|
||||||
|
console.log('User Registered to Signalr Hub');
|
||||||
|
cb(connection);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerNotificationHandlers(connection) {
|
||||||
|
connection.on("UpdatedOrderState", (message) => {
|
||||||
|
toastr.success('Updated to status: ' + message.status, 'Order Id: ' + message.orderId);
|
||||||
|
if (window.location.pathname.split("/").pop() === 'Order') {
|
||||||
|
refreshOrderList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getToken() {
|
||||||
|
return '@Context.GetTokenAsync("access_token").Result';
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshOrderList() {
|
||||||
|
clearTimeout(timerId);
|
||||||
|
timerId = setTimeout(function () {
|
||||||
|
window.location.reload();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
63
src/Web/WebUI/Views/Shared/_LoginPartial.cshtml
Normal file
63
src/Web/WebUI/Views/Shared/_LoginPartial.cshtml
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
@using Microsoft.AspNetCore.Identity
|
||||||
|
@using Microsoft.eShopOnContainers.WebUI.ViewModels
|
||||||
|
@using Microsoft.eShopOnContainers.WebUI.Services
|
||||||
|
|
||||||
|
@inject IIdentityParser<ApplicationUser> UserManager
|
||||||
|
|
||||||
|
@*@if (Context.User.Identity.IsAuthenticated)*@
|
||||||
|
@if (User.FindFirst(x => x.Type == "preferred_username") != null)
|
||||||
|
{
|
||||||
|
<section class="col-lg-4 col-md-5 col-xs-12">
|
||||||
|
<div class="esh-identity">
|
||||||
|
<form asp-area="" asp-controller="Account" asp-action="SignOut" method="post" id="logoutForm" class="navbar-right">
|
||||||
|
<section class="esh-identity-section">
|
||||||
|
|
||||||
|
<div class="esh-identity-name">@User.FindFirst(x => x.Type == "preferred_username").Value</div>
|
||||||
|
<img class="esh-identity-image" src="~/images/arrow-down.png">
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="esh-identity-drop">
|
||||||
|
|
||||||
|
<a class="esh-identity-item"
|
||||||
|
asp-controller="Order"
|
||||||
|
asp-action="Index">
|
||||||
|
|
||||||
|
<div class="esh-identity-name esh-identity-name--upper">My orders</div>
|
||||||
|
<img class="esh-identity-image" src="~/images/my_orders.png">
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="esh-identity-item"
|
||||||
|
href="javascript:document.getElementById('logoutForm').submit()">
|
||||||
|
|
||||||
|
<div class="esh-identity-name esh-identity-name--upper">Log Out</div>
|
||||||
|
<img class="esh-identity-image" src="~/images/logout.png">
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="col-lg-1 col-xs-12">
|
||||||
|
@await Component.InvokeAsync("Cart", new { user = UserManager.Parse(User) })
|
||||||
|
</section>
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<section class="col-lg-4 col-md-5 col-xs-12">
|
||||||
|
<div class="esh-identity">
|
||||||
|
<section class="esh-identity-section">
|
||||||
|
<div class="esh-identity-item">
|
||||||
|
|
||||||
|
<a asp-area="" asp-controller="Account" asp-action="SignIn" class="esh-identity-name esh-identity-name--upper">
|
||||||
|
Login
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="col-lg-1 col-xs-12">
|
||||||
|
|
||||||
|
</section>
|
||||||
|
}
|
14
src/Web/WebUI/Views/Shared/_ValidationScriptsPartial.cshtml
Normal file
14
src/Web/WebUI/Views/Shared/_ValidationScriptsPartial.cshtml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<environment names="Development">
|
||||||
|
<script src="~/lib/jquery-validate/jquery.validate.js"></script>
|
||||||
|
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
|
||||||
|
</environment>
|
||||||
|
<environment names="Staging,Production">
|
||||||
|
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js"
|
||||||
|
asp-fallback-src="~/lib/jquery-validate/jquery.validate.min.js"
|
||||||
|
asp-fallback-test="window.jQuery && window.jQuery.validator">
|
||||||
|
</script>
|
||||||
|
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.6/jquery.validate.unobtrusive.min.js"
|
||||||
|
asp-fallback-src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
|
||||||
|
asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive">
|
||||||
|
</script>
|
||||||
|
</environment>
|
5
src/Web/WebUI/Views/_ViewImports.cshtml
Normal file
5
src/Web/WebUI/Views/_ViewImports.cshtml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
@using Microsoft.eShopOnContainers.WebUI
|
||||||
|
@using Microsoft.eShopOnContainers.WebUI.ViewModels
|
||||||
|
@using Microsoft.AspNetCore.Identity
|
||||||
|
|
||||||
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
3
src/Web/WebUI/Views/_ViewStart.cshtml
Normal file
3
src/Web/WebUI/Views/_ViewStart.cshtml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@{
|
||||||
|
Layout = "_Layout";
|
||||||
|
}
|
81
src/Web/WebUI/WebUI.csproj
Normal file
81
src/Web/WebUI/WebUI.csproj
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<UserSecretsId>aspnet-Microsoft.eShopOnContainers-946ae052-8305-4a99-965b-ec8636ddbae3</UserSecretsId>
|
||||||
|
<DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
|
||||||
|
<TypeScriptToolsVersion>3.0</TypeScriptToolsVersion>
|
||||||
|
<GenerateErrorForMissingTargetingPacks>false</GenerateErrorForMissingTargetingPacks>
|
||||||
|
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Setup\images.zip">
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="Setup\override.css">
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="5.0.1" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="5.0.1" />
|
||||||
|
<PackageReference Include="BuildBundlerMinifier" Version="3.2.449" />
|
||||||
|
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.18.0" />
|
||||||
|
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.18.0" />
|
||||||
|
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="2.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.8.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" />
|
||||||
|
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
|
||||||
|
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3" />
|
||||||
|
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.1-dev-00216" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0-dev-00834" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Http" Version="7.2.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Seq" Version="4.1.0-dev-00166" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="ViewModels\CampaignItem.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\BuildingBlocks\Devspaces.Support\Devspaces.Support.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Update="appsettings.Development.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="appsettings.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="bundleconfig.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="compilerconfig.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="web.config">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
7
src/Web/WebUI/appsettings.Development.json
Normal file
7
src/Web/WebUI/appsettings.Development.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"Serilog": {
|
||||||
|
"MinimumLevel": {
|
||||||
|
"Default": "Debug"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
src/Web/WebUI/appsettings.json
Normal file
25
src/Web/WebUI/appsettings.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"CatalogUrl": "http://localhost:5101",
|
||||||
|
"OrderingUrl": "http://localhost:5102",
|
||||||
|
"BasketUrl": "http://localhost:5103",
|
||||||
|
"IdentityUrl": "http://localhost:5105",
|
||||||
|
"CallBackUrl": "http://localhost:5100/",
|
||||||
|
"IsClusterEnv": "False",
|
||||||
|
"UseResilientHttp": "True",
|
||||||
|
"UseLoadTest": false,
|
||||||
|
"ActivateCampaignDetailFunction": "False",
|
||||||
|
"UseCustomizationData": false,
|
||||||
|
"Serilog": {
|
||||||
|
"SeqServerUrl": null,
|
||||||
|
"LogstashgUrl": null,
|
||||||
|
"MinimumLevel": {
|
||||||
|
"Default": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ApplicationInsights": {
|
||||||
|
"InstrumentationKey": ""
|
||||||
|
},
|
||||||
|
"HttpClientRetryCount": 8,
|
||||||
|
"HttpClientExceptionsAllowedBeforeBreaking": 7,
|
||||||
|
"SessionCookieLifetimeMinutes": 60
|
||||||
|
}
|
38
src/Web/WebUI/bundleconfig.json
Normal file
38
src/Web/WebUI/bundleconfig.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Configure bundling and minification for the project.
|
||||||
|
// More info at https://go.microsoft.com/fwlink/?LinkId=808241
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"outputFileName": "wwwroot/css/site.min.css",
|
||||||
|
// An array of relative input file paths. Globbing patterns supported
|
||||||
|
"inputFiles": [
|
||||||
|
"wwwroot/css/**/*.css"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFileName": "wwwroot/js/site.js",
|
||||||
|
"inputFiles": [
|
||||||
|
"wwwroot/lib/@microsoft/signalr/dist/browser/signalr.js",
|
||||||
|
"wwwroot/lib/toastr/toastr.min.js"
|
||||||
|
],
|
||||||
|
// Optionally specify minification options
|
||||||
|
"minify": {
|
||||||
|
"enabled": false,
|
||||||
|
"renameLocals": true
|
||||||
|
},
|
||||||
|
// Optinally generate .map file
|
||||||
|
"sourceMap": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFileName": "wwwroot/js/site.min.js",
|
||||||
|
"inputFiles": [
|
||||||
|
"wwwroot/js/site.js"
|
||||||
|
],
|
||||||
|
// Optionally specify minification options
|
||||||
|
"minify": {
|
||||||
|
"enabled": false,
|
||||||
|
"renameLocals": true
|
||||||
|
},
|
||||||
|
// Optinally generate .map file
|
||||||
|
"sourceMap": false
|
||||||
|
}
|
||||||
|
]
|
42
src/Web/WebUI/compilerconfig.json
Normal file
42
src/Web/WebUI/compilerconfig.json
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/orders/orders.component.css",
|
||||||
|
"inputFile": "wwwroot/css/orders/orders.component.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/orders/orders-new/orders-new.component.css",
|
||||||
|
"inputFile": "wwwroot/css/orders/orders-new/orders-new.component.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/orders/orders-detail/orders-detail.component.css",
|
||||||
|
"inputFile": "wwwroot/css/orders/orders-detail/orders-detail.component.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/catalog/catalog.component.css",
|
||||||
|
"inputFile": "wwwroot/css/catalog/catalog.component.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/basket/basket.component.css",
|
||||||
|
"inputFile": "wwwroot/css/basket/basket.component.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/basket/basket-status/basket-status.component.css",
|
||||||
|
"inputFile": "wwwroot/css/basket/basket-status/basket-status.component.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/shared/components/header/header.css",
|
||||||
|
"inputFile": "wwwroot/css/shared/components/header/header.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/shared/components/identity/identity.css",
|
||||||
|
"inputFile": "wwwroot/css/shared/components/identity/identity.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/shared/components/pager/pager.css",
|
||||||
|
"inputFile": "wwwroot/css/shared/components/pager/pager.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/app.component.css",
|
||||||
|
"inputFile": "wwwroot/css/app.component.scss"
|
||||||
|
}
|
||||||
|
]
|
49
src/Web/WebUI/compilerconfig.json.defaults
Normal file
49
src/Web/WebUI/compilerconfig.json.defaults
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"compilers": {
|
||||||
|
"less": {
|
||||||
|
"autoPrefix": "",
|
||||||
|
"cssComb": "none",
|
||||||
|
"ieCompat": true,
|
||||||
|
"strictMath": false,
|
||||||
|
"strictUnits": false,
|
||||||
|
"relativeUrls": true,
|
||||||
|
"rootPath": "",
|
||||||
|
"sourceMapRoot": "",
|
||||||
|
"sourceMapBasePath": "",
|
||||||
|
"sourceMap": false
|
||||||
|
},
|
||||||
|
"sass": {
|
||||||
|
"includePath": "",
|
||||||
|
"indentType": "space",
|
||||||
|
"indentWidth": 2,
|
||||||
|
"outputStyle": "expanded",
|
||||||
|
"Precision": 5,
|
||||||
|
"relativeUrls": true,
|
||||||
|
"sourceMapRoot": "",
|
||||||
|
"sourceMap": false
|
||||||
|
},
|
||||||
|
"stylus": {
|
||||||
|
"sourceMap": false
|
||||||
|
},
|
||||||
|
"babel": {
|
||||||
|
"sourceMap": false
|
||||||
|
},
|
||||||
|
"coffeescript": {
|
||||||
|
"bare": false,
|
||||||
|
"runtimeMode": "node",
|
||||||
|
"sourceMap": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minifiers": {
|
||||||
|
"css": {
|
||||||
|
"enabled": true,
|
||||||
|
"termSemicolons": true,
|
||||||
|
"gzip": false
|
||||||
|
},
|
||||||
|
"javascript": {
|
||||||
|
"enabled": true,
|
||||||
|
"termSemicolons": true,
|
||||||
|
"gzip": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
src/Web/WebUI/globalusings.cs
Normal file
51
src/Web/WebUI/globalusings.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
global using Devspaces.Support;
|
||||||
|
global using HealthChecks.UI.Client;
|
||||||
|
global using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
|
global using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
global using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||||
|
global using Microsoft.AspNetCore.Authentication;
|
||||||
|
global using Microsoft.AspNetCore.Authorization;
|
||||||
|
global using Microsoft.AspNetCore.Builder;
|
||||||
|
global using Microsoft.AspNetCore.DataProtection;
|
||||||
|
global using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
||||||
|
global using Microsoft.AspNetCore.Hosting;
|
||||||
|
global using Microsoft.AspNetCore.Http;
|
||||||
|
global using Microsoft.AspNetCore.Identity;
|
||||||
|
global using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
|
global using Microsoft.AspNetCore.Mvc;
|
||||||
|
global using Microsoft.AspNetCore;
|
||||||
|
global using Microsoft.eShopOnContainers.WebUI.Services;
|
||||||
|
global using Microsoft.eShopOnContainers.WebUI.ViewModels.Annotations;
|
||||||
|
global using Microsoft.eShopOnContainers.WebUI.ViewModels.CartViewModels;
|
||||||
|
global using Microsoft.eShopOnContainers.WebUI.ViewModels.CatalogViewModels;
|
||||||
|
global using Microsoft.eShopOnContainers.WebUI.ViewModels.Pagination;
|
||||||
|
global using Microsoft.eShopOnContainers.WebUI.ViewModels;
|
||||||
|
global using Microsoft.eShopOnContainers.WebUI;
|
||||||
|
global using Microsoft.Extensions.Configuration;
|
||||||
|
global using Microsoft.Extensions.DependencyInjection;
|
||||||
|
global using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||||
|
global using Microsoft.Extensions.Hosting;
|
||||||
|
global using Microsoft.Extensions.Logging;
|
||||||
|
global using Microsoft.Extensions.Options;
|
||||||
|
global using Microsoft.IdentityModel.Logging;
|
||||||
|
global using Serilog;
|
||||||
|
global using StackExchange.Redis;
|
||||||
|
global using System.Collections.Generic;
|
||||||
|
global using System.ComponentModel.DataAnnotations;
|
||||||
|
global using System.ComponentModel;
|
||||||
|
global using System.IdentityModel.Tokens.Jwt;
|
||||||
|
global using System.IO.Compression;
|
||||||
|
global using System.IO;
|
||||||
|
global using System.Linq;
|
||||||
|
global using System.Net.Http.Headers;
|
||||||
|
global using System.Net.Http;
|
||||||
|
global using System.Security.Claims;
|
||||||
|
global using System.Security.Principal;
|
||||||
|
global using System.Text.Json.Serialization;
|
||||||
|
global using System.Text.Json;
|
||||||
|
global using System.Text;
|
||||||
|
global using System.Threading.Tasks;
|
||||||
|
global using System.Threading;
|
||||||
|
global using System;
|
||||||
|
global using WebUI.Infrastructure;
|
||||||
|
global using WebUI.Services.ModelDTOs;
|
3
src/Web/WebUI/values.dev.yaml
Normal file
3
src/Web/WebUI/values.dev.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
inf:
|
||||||
|
k8s:
|
||||||
|
dns: $(spacePrefix)identity-api$(hostSuffix)
|
13
src/Web/WebUI/web.config
Normal file
13
src/Web/WebUI/web.config
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
|
||||||
|
-->
|
||||||
|
<system.webServer>
|
||||||
|
<handlers>
|
||||||
|
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
|
||||||
|
</handlers>
|
||||||
|
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
|
||||||
|
</system.webServer>
|
||||||
|
</configuration>
|
6
src/Web/WebUI/wwwroot/_references.js
Normal file
6
src/Web/WebUI/wwwroot/_references.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/// <autosync enabled="true" />
|
||||||
|
/// <reference path="js/site.js" />
|
||||||
|
/// <reference path="lib/jquery/dist/jquery.js" />
|
||||||
|
/// <reference path="lib/jquery-validation/dist/jquery.validate.js" />
|
||||||
|
/// <reference path="lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js" />
|
||||||
|
/// <reference path="lib/tether/dist/js/tether.js" />
|
65
src/Web/WebUI/wwwroot/css/_variables.scss
Normal file
65
src/Web/WebUI/wwwroot/css/_variables.scss
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Colors
|
||||||
|
$color-brand: #00A69C;
|
||||||
|
$color-brand-dark: darken($color-brand, 10%);
|
||||||
|
$color-brand-darker: darken($color-brand, 20%);
|
||||||
|
$color-brand-bright: lighten($color-brand, 10%);
|
||||||
|
$color-brand-brighter: lighten($color-brand, 20%);
|
||||||
|
|
||||||
|
$color-secondary: #83D01B;
|
||||||
|
$color-secondary-dark: darken($color-secondary, 5%);
|
||||||
|
$color-secondary-darker: darken($color-secondary, 20%);
|
||||||
|
$color-secondary-bright: lighten($color-secondary, 10%);
|
||||||
|
$color-secondary-brighter: lighten($color-secondary, 20%);
|
||||||
|
|
||||||
|
$color-warning: #ff0000;
|
||||||
|
$color-warning-dark: darken($color-warning, 5%);
|
||||||
|
$color-warning-darker: darken($color-warning, 20%);
|
||||||
|
$color-warning-bright: lighten($color-warning, 10%);
|
||||||
|
$color-warning-brighter: lighten($color-warning, 20%);
|
||||||
|
|
||||||
|
|
||||||
|
$color-background-dark: #333333;
|
||||||
|
$color-background-darker: #000000;
|
||||||
|
$color-background-bright: #EEEEFF;
|
||||||
|
$color-background-brighter: #FFFFFF;
|
||||||
|
|
||||||
|
$color-foreground-dark: #333333;
|
||||||
|
$color-foreground-darker: #000000;
|
||||||
|
$color-foreground-bright: #EEEEEE;
|
||||||
|
$color-foreground-brighter: #FFFFFF;
|
||||||
|
|
||||||
|
// Animations
|
||||||
|
$animation-speed-default: .35s;
|
||||||
|
$animation-speed-slow: .5s;
|
||||||
|
$animation-speed-fast: .15s;
|
||||||
|
|
||||||
|
// Fonts
|
||||||
|
$font-weight-light: 200;
|
||||||
|
$font-weight-semilight: 300;
|
||||||
|
$font-weight-normal: 400;
|
||||||
|
$font-weight-semibold: 600;
|
||||||
|
$font-weight-bold: 700;
|
||||||
|
|
||||||
|
$font-size-xs: .65rem; // 10.4px
|
||||||
|
$font-size-s: .85rem; // 13.6px
|
||||||
|
$font-size-m: 1rem; // 16px
|
||||||
|
$font-size-l: 1.25rem; // 20px
|
||||||
|
$font-size-xl: 1.5rem; // 24px
|
||||||
|
|
||||||
|
// Medias
|
||||||
|
$media-screen-xxs: 360px;
|
||||||
|
$media-screen-xs: 640px;
|
||||||
|
$media-screen-s: 768px;
|
||||||
|
$media-screen-m: 1024px;
|
||||||
|
$media-screen-l: 1280px;
|
||||||
|
$media-screen-xl: 1440px;
|
||||||
|
$media-screen-xxl: 1680px;
|
||||||
|
$media-screen-xxxl: 1920px;
|
||||||
|
|
||||||
|
// Borders
|
||||||
|
$border-light: 1px;
|
||||||
|
|
||||||
|
// Images
|
||||||
|
$image_path: '../../images/';
|
||||||
|
$image-main_banner: '#{$image_path}main_banner.png';
|
||||||
|
$image-arrow_down: '#{$image_path}arrow-down.png';
|
18
src/Web/WebUI/wwwroot/css/app.component.css
Normal file
18
src/Web/WebUI/wwwroot/css/app.component.css
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
.esh-app-footer {
|
||||||
|
background-color: #000000;
|
||||||
|
border-top: 1px solid #EEEEEE;
|
||||||
|
margin-top: 2.5rem;
|
||||||
|
padding-bottom: 2.5rem;
|
||||||
|
padding-top: 2.5rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.esh-app-footer-brand {
|
||||||
|
height: 50px;
|
||||||
|
width: 230px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.esh-app-header {
|
||||||
|
margin: 15px;
|
||||||
|
}
|
||||||
|
|
1
src/Web/WebUI/wwwroot/css/app.component.min.css
vendored
Normal file
1
src/Web/WebUI/wwwroot/css/app.component.min.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.esh-app-footer{background-color:#000;border-top:1px solid #eee;margin-top:2.5rem;padding-bottom:2.5rem;padding-top:2.5rem;width:100%;}.esh-app-footer-brand{height:50px;width:230px;}.esh-app-header{margin:15px;}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user