Skip to content

Commit

Permalink
feat(Web): ef core / sqlite integration
Browse files Browse the repository at this point in the history
  • Loading branch information
seangwright committed Nov 6, 2022
1 parent 83c530e commit 35a0ba1
Show file tree
Hide file tree
Showing 15 changed files with 355 additions and 117 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,5 @@ MigrationBackup/

# Ionide (cross platform F# VS Code tools) working folder
.ionide/

**/App_Data/FShopOnWeb*
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"recommendations": [
"Ionide.Ionide-fsharp",
"esbenp.prettier-vscode",
"EditorConfig.EditorConfig"
"EditorConfig.EditorConfig",
"alexcvzz.vscode-sqlite"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": []
Expand Down
Empty file.
2 changes: 1 addition & 1 deletion src/Microsoft.eShopWeb.Web/Basket/Basket.Domain.fs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ module BasketDomain =

let productFallbackImageUri = "/images/brand.png"

let basket =
let basketFromCatalog catalogItems =
{ Id = 1
Items = List.mapi mapCatalogItem catalogItems
BuyerId = None }
17 changes: 13 additions & 4 deletions src/Microsoft.eShopWeb.Web/Basket/Basket.Page.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ namespace Microsoft.eShopWeb.Web.Basket
open Falco
open Falco.Markup.Attr
open Falco.Markup.Elem
open Microsoft.eShopWeb.Web.Persistence
open Microsoft.eShopWeb.Web
open System.Linq

module BasketPage =
module private Template =
Expand All @@ -12,9 +14,16 @@ module BasketPage =

let head = PublicLayout.head metadata

let body =
PublicLayout.body [ div [ class' "container" ] (BasketComponent.cmpt BasketDomain.basket) ]
let body basket =
PublicLayout.body [ div [ class' "container" ] (BasketComponent.cmpt basket) ]

let page = PublicLayout.layout head body
let page basket = PublicLayout.layout head (body basket)

let handler: HttpHandler = Response.ofHtml Template.page
let handler: HttpHandler =
Services.inject<ShopContext> (fun context ->
let dbItems = context.CatalogItems.ToList()

let items = List.ofSeq (dbItems)
let basket = BasketDomain.basketFromCatalog items

Response.ofHtml (Template.page basket))
56 changes: 14 additions & 42 deletions src/Microsoft.eShopWeb.Web/Domain.fs
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
namespace Microsoft.eShopWeb.Web

open System
open System.ComponentModel.DataAnnotations

module Domain =

type CatalogBrand = { Id: int; Brand: string }
[<CLIMutable>]
type CatalogBrand =
{ [<Key>]
Id: int
Name: string }

type CatalogType = { Id: int; Type: string }
[<CLIMutable>]
type CatalogType =
{ [<Key>]
Id: int
Name: string }

[<CLIMutable>]
type CatalogItem =
{ Id: Guid
{ [<Key>]
Id: Guid
Name: string
Description: string
PictureUri: string
Expand All @@ -18,42 +29,3 @@ module Domain =
CatalogTypeId: int
CatalogBrand: CatalogBrand
CatalogType: CatalogType }

let catalogBrand: CatalogBrand = { Id = 1; Brand = "Brand" }

let catalogType: CatalogType = { Id = 1; Type = "Type" }

let catalogItem1: CatalogItem =
{ Id = Guid.Parse("dd3334a9-a0da-4bd1-b819-b64e5036d614")
Name = "Hoodie"
Description = "Description"
PictureUri = "/images/products/1.png"
Price = 1.50M
CatalogBrandId = 1
CatalogTypeId = 1
CatalogBrand = catalogBrand
CatalogType = catalogType }

let catalogItem2: CatalogItem =
{ Id = Guid.NewGuid()
Name = "Mug"
Description = "Description"
PictureUri = "/images/products/2.png"
Price = 1.0M
CatalogBrandId = 1
CatalogTypeId = 1
CatalogBrand = catalogBrand
CatalogType = catalogType }

let catalogItem3: CatalogItem =
{ Id = Guid.NewGuid()
Name = "T-Shirt"
Description = "Description"
PictureUri = "/images/products/3.png"
Price = 1.0M
CatalogBrandId = 1
CatalogTypeId = 1
CatalogBrand = catalogBrand
CatalogType = catalogType }

let catalogItems = [ catalogItem1; catalogItem2; catalogItem3 ]
24 changes: 16 additions & 8 deletions src/Microsoft.eShopWeb.Web/Home/CatalogFilters.Component.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,48 @@ open Falco.Markup
open Falco.Markup.Attr
open Falco.Markup.Elem
open Falco.Markup.Text
open Microsoft.eShopWeb.Web.Home

module CatalogFiltersComponent =
type Props =
{ Brands: string list
Types: string list }

module private Template =

let allOption = option [] [ raw "All" ]

let optionTmpl index v =
option [ value $"{index}" ] [ raw $"{v}" ]

let brandSelect =
let brandOptions = [ allOption ] @ List.mapi optionTmpl HomeDomain.brands
let brandSelect (brands: string list) =
let brandOptions = [ allOption ] @ List.mapi optionTmpl brands

select
[ class' "esh-catalog-filter"
id "CatalogModel_BrandFilterApplied"
name "CatalogModel.BrandFilterApplied" ]
brandOptions

let typesSelect =
let typeOptions = [ allOption ] @ List.mapi optionTmpl HomeDomain.types
let typesSelect (types: string list) =
let typeOptions = [ allOption ] @ List.mapi optionTmpl types

select
[ class' "esh-catalog-filter"
id "CatalogModel_TypesFilterApplied"
name "CatalogModel.TypesFilterApplied" ]
typeOptions

let cmpt =
let cmpt props =
section
[ class' "esh-catalog-filters" ]
[ div
[ class' "container" ]
[ form
[ method "get" ]
[ label [ class' "esh-catalog-label"; Attr.create "data-title" "brand" ] [ Template.brandSelect ]
label [ class' "esh-catalog-label"; Attr.create "data-title" "type" ] [ Template.typesSelect ]
[ label
[ class' "esh-catalog-label"; Attr.create "data-title" "brand" ]
[ Template.brandSelect props.Brands ]
label
[ class' "esh-catalog-label"; Attr.create "data-title" "type" ]
[ Template.typesSelect props.Types ]
input [ src "/images/arrow-right.svg"; class' "esh-catalog-send"; type' "image" ] ] ] ]
6 changes: 4 additions & 2 deletions src/Microsoft.eShopWeb.Web/Home/CatalogGrid.Component.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ open Falco.Markup.Text
open Microsoft.eShopWeb.Web.Domain

module CatalogGridComponent =
type Props = { CatalogItems: CatalogItem list }

module private Template =
let itemTmpl index item =
div
Expand All @@ -21,5 +23,5 @@ module CatalogGridComponent =
// TODO - figure out how to generate an XSRF token (@Html.AntiForgeryToken() in Razor)
] ]

let cmpt =
div [ class' "esh-catalog-items row" ] (List.mapi Template.itemTmpl catalogItems)
let cmpt props =
div [ class' "esh-catalog-items row" ] (List.mapi Template.itemTmpl props.CatalogItems)
33 changes: 18 additions & 15 deletions src/Microsoft.eShopWeb.Web/Home/CatalogPager.Component.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,29 @@ namespace Microsoft.eShopWeb.Web.Home
open Falco.Markup.Attr
open Falco.Markup.Elem
open Falco.Markup.Text
open Microsoft.eShopWeb.Web.Domain

module CatalogPagerComponent =
type Props = { CurrentPage: int; ItemsCount: int }

module private Template =
let currentPage = 1
let pageSize = 10
let totalPages = (catalogItems.Length / pageSize) + 1
let morePagesAvailable = catalogItems.Length > pageSize
let hasPreviousPage = currentPage > 1
let hasNextPage = currentPage < totalPages

let totalItemsOnPage =
match morePagesAvailable with
| true -> pageSize
| false -> catalogItems.Length
let pagingText props =
let currentPage = 1
let pageSize = 10
let totalPages = (props.ItemsCount / pageSize) + 1
let morePagesAvailable = props.ItemsCount > pageSize
let hasPreviousPage = currentPage > 1
let hasNextPage = currentPage < totalPages

let currentItemIndex = (pageSize * (currentPage - 1)) + 1
let totalItemsOnPage =
match morePagesAvailable with
| true -> pageSize
| false -> props.ItemsCount

let pagingText =
let currentItemIndex = (pageSize * (currentPage - 1)) + 1
sprintf "Showing %d of %d products - Page %d - %d" currentItemIndex totalItemsOnPage currentPage totalPages

let cmpt =
let cmpt props =
div
[ class' "esh-pager" ]
[ div
Expand All @@ -39,7 +40,9 @@ module CatalogPagerComponent =
[ class' "esh-pager-item-left esh-pager-item--navigable esh-pager-item is-disabled"
href "/?pageId=-1" ]
[ raw "Previous" ] ]
div [ class' "col-md-8 col-xs-12" ] [ span [ class' "esh-pager-item" ] [ raw Template.pagingText ] ]
div
[ class' "col-md-8 col-xs-12" ]
[ span [ class' "esh-pager-item" ] [ raw (Template.pagingText props) ] ]
div
[ class' "col-md-2 col-xs-12" ]
[ a
Expand Down
6 changes: 0 additions & 6 deletions src/Microsoft.eShopWeb.Web/Home/Home.Domain.fs

This file was deleted.

43 changes: 36 additions & 7 deletions src/Microsoft.eShopWeb.Web/Home/Home.Page.fs
Original file line number Diff line number Diff line change
@@ -1,26 +1,55 @@
namespace Microsoft.eShopWeb.Web.Home

open Microsoft.eShopWeb.Web.Domain
open Falco
open Falco.Markup.Attr
open Falco.Markup.Elem
open Microsoft.eShopWeb.Web
open Microsoft.eShopWeb.Web.Home
open Microsoft.eShopWeb.Web.Persistence
open System.Linq
open Microsoft.EntityFrameworkCore

module HomePage =
type Props =
{ FiltersProps: CatalogFiltersComponent.Props
GridProps: CatalogGridComponent.Props
PagerProps: CatalogPagerComponent.Props }

module private Template =
let metadata: PublicLayout.HeadMetadata = { Title = "Home"; Description = "" }

let head = PublicLayout.head metadata

let body =
let body props =
PublicLayout.body
[ CatalogFiltersComponent.cmpt
[ CatalogFiltersComponent.cmpt props.FiltersProps
div
[ class' "container" ]
[ CatalogPagerComponent.cmpt
CatalogGridComponent.cmpt
CatalogPagerComponent.cmpt ] ]
[ CatalogPagerComponent.cmpt props.PagerProps
CatalogGridComponent.cmpt props.GridProps
CatalogPagerComponent.cmpt props.PagerProps ] ]

let page props = PublicLayout.layout head (body props)

let handler: HttpHandler =
Services.inject<ShopContext> (fun context ->
let dbItems =
context
.CatalogItems
.Include(fun i -> i.CatalogBrand)
.Include(fun i -> i.CatalogType)
.ToList()

let items = List.ofSeq (dbItems)
let brands = List.map (fun i -> i.CatalogBrand.Name) items |> List.distinct
let types = List.map (fun i -> i.CatalogType.Name) items |> List.distinct

let page = PublicLayout.layout head body
let props =
{ FiltersProps = { Types = types; Brands = brands }
GridProps = { CatalogItems = items }
PagerProps =
{ ItemsCount = items.Length
CurrentPage = 1 } }

let handler: HttpHandler = Response.ofHtml Template.page
Response.ofHtml (Template.page props))
5 changes: 3 additions & 2 deletions src/Microsoft.eShopWeb.Web/Microsoft.eShopWeb.Web.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="Domain.fs" />
<Compile Include="Persistence.fs" />
<Compile Include="Layout\Public.Layout.fs" />
<Compile Include="Home\Home.Domain.fs" />
<Compile Include="Home\CatalogFilters.Component.fs" />
<Compile Include="Home\CatalogGrid.Component.fs" />
<Compile Include="Home\CatalogPager.Component.fs" />
Expand All @@ -20,6 +20,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="EntityFrameworkCore.FSharp" Version="6.0.7" />
<PackageReference Include="Falco" Version="3.1.14" />
<PackageReference Include="Falco" Version="4.0.0-rc1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.10" />
</ItemGroup>
</Project>
Loading

0 comments on commit 35a0ba1

Please sign in to comment.