namespace ShoppingCart
open IntelliFactory.Html
open IntelliFactory.WebSharper
open IntelliFactory.WebSharper.Sitelets
type Action =
| Home
module EventHub =
type Item =
{ ProductName : string
Title: string
Price: float }
type CartItem =
{ Item: Item
Quantity: int
Order: int }
type CartEvent =
| AddItem of Item * int
| AddOneItem of string
| RemoveOneItem of string
| RemoveItem of string
| EmptyCart
[]
let ShoppingCartEvents = new Event ()
module Server =
type CartFamily =
{ Title : string
Products : CartProduct list }
and CartProduct =
{ Title : string
ProductName : string
ImageSrc : string
Price : float }
/// Computes the families and the products inside them that are
/// available for download.
[]
let ComputeFamiliesAndProducts () : CartFamily list =
let T imageSrc (title, id, price) =
{ Title = title
ProductName = id
ImageSrc = imageSrc
Price = price }
let laptop prod = T "/images/laptop.png" prod
let desktop prod = T "/images/desktop.png" prod
let netbook prod = T "/images/netbook.png" prod
[ { Title = "Laptops"
Products =
[ laptop ("Toshiba", "id1", 1299.0)
laptop ("HP", "id2", 1499.0)
laptop ("Dell", "id3", 1499.0)
laptop ("Acer", "id4", 1499.0) ] }
{ Title = "Desktops"
Products =
[ desktop ("Gamer 1", "id11", 699.0)
desktop ("Gamer 2", "id12", 799.0)
desktop ("Office", "id13", 599.0)
desktop ("Server", "id14", 1299.0) ] }
{ Title = "Netbooks"
Products =
[ netbook ("Entry", "id21", 799.0)
netbook ("Medium", "id22", 899.0)
netbook ("Cool", "id23", 699.0)
netbook ("Speed-King", "id24", 999.0) ] } ]
[]
module Internals =
let private Items = ref Map.empty
let private Counter = ref 0
let GetItemsInCart () =
async {
return !Items
}
let NextCounter () =
async {
Counter := !Counter + 1
return !Counter
}
let SetItemsInCart items =
Items := items
[]
module Client =
open IntelliFactory.WebSharper.Html
module WebControls =
let Button label =
Input [Attr.Type "button"; Attr.Value label; Width (label.Length*15 |> string)]
/// Sends payment.
[]
let SendPayment items =
()
/// Displays all item categories and their items for sale.
[]
let ItemsToBuy () =
let families = Server.ComputeFamiliesAndProducts ()
Div [Id "shopping-cart"] -< [
families
|> List.map (fun family ->
Div [Attr.Class "family"] -< [
H1 [Text family.Title]
family.Products
|> List.map (fun product ->
let input = Input [Attr.Type "text"; Attr.Value "1"]
Div [Attr.Class "product"] -< [
Img [Alt product.Title; Src product.ImageSrc]
Div [
H1 [Text product.Title]
P [
Code [Text ("$" + string product.Price)]
] -< [
Text " / item"
]
P [
Text "Quantity:"
] -< [
input
]
WebControls.Button "Add to cart"
|>! OnClick (fun e args ->
EventHub.CartEvent.AddItem (
{ ProductName=product.ProductName
Title=product.Title
Price=product.Price },
(int input.Value))
|> EventHub.ShoppingCartEvents.Trigger
)
]
]
)
|> fun products ->
products @ [
Div [Attr.Style "clear:both;"]
]
|> Div
]
)
|> Div
]
/// Displays the shopping cart.
[]
let ShoppingCart () =
let contents = Div []
let updateCart () =
async {
let! itemsInCart = Internals.GetItemsInCart ()
contents.Clear ()
Div [Id "shopping-cart-contents"] -< [
H1 [Text "Shopping Cart"]
Table [
itemsInCart
|> Map.toList
|> List.map snd
|> List.sortBy (fun cartItem -> cartItem.Order)
|> List.fold (fun (i, acc, sum) cartItem ->
let alt =
if i%2 = 1 then
"alt "
else
""
(i+1, acc @
[
TR [Attr.Class alt] -< [
TD [Attr.Class "col1"] -< [
WebControls.Button "-"
// What happens when we click "-"?
|>! OnClick (fun e args ->
cartItem.Item.ProductName
|> EventHub.CartEvent.RemoveOneItem
|> EventHub.ShoppingCartEvents.Trigger
)
Div [Attr.Class "count"] -< [cartItem.Quantity |> string |> Text]
WebControls.Button "+"
// What happens when we click "+"?
|>! OnClick (fun e args ->
cartItem.Item.ProductName
|> EventHub.CartEvent.AddOneItem
|> EventHub.ShoppingCartEvents.Trigger
)
WebControls.Button "X"
// What happens when we click "Remove"?
|>! OnClick (fun e args ->
cartItem.Item.ProductName
|> EventHub.CartEvent.RemoveItem
|> EventHub.ShoppingCartEvents.Trigger
)
Div [Attr.Class "title"] -< [Text cartItem.Item.Title]
]
TD [Attr.Class "col2"] -< [
Code [
Text (string cartItem.Item.Price)
]
]
]
], sum + (float cartItem.Quantity)*cartItem.Item.Price)
) (0, [], 0.)
|> fun (_, rows, sum) ->
rows
|> fun rows ->
rows @ [
TR [ Hr [] ]
TR [
TD [Text "Total:"]
TD [Code [sum |> string |> Text]]
]
]
|> TBody
]
Div [Attr.Style "height:20px"] -< []
Div [
WebControls.Button "Checkout"
|>! OnClick (fun e args ->
JavaScript.Alert "Checkout!"
// Process payment
itemsInCart
|> SendPayment
// Empty shopping cart
EventHub.CartEvent.EmptyCart
|> EventHub.ShoppingCartEvents.Trigger
)
WebControls.Button "Empty cart"
|>! OnClick (fun e args ->
EventHub.CartEvent.EmptyCart
|> EventHub.ShoppingCartEvents.Trigger
)
]
]
|> contents.Append
}
|> Async.Start
EventHub.ShoppingCartEvents.Publish.Add (fun ce ->
async {
let! itemsInCart = Internals.GetItemsInCart ()
let! next = Internals.NextCounter ()
let SET items =
items |> Internals.SetItemsInCart
let _ =
match ce with
| EventHub.CartEvent.AddItem (item, qty) ->
let cartItem =
if Map.containsKey item.ProductName itemsInCart then
let cartItem = itemsInCart.[item.ProductName]
{ cartItem with Quantity = cartItem.Quantity+qty }
else
{ Item = item; Quantity = qty; Order = next }
itemsInCart
|> Map.add item.ProductName cartItem
|> SET
| EventHub.CartEvent.RemoveOneItem productName ->
if Map.containsKey productName itemsInCart then
let cartItem = itemsInCart.[productName]
if cartItem.Quantity <= 1 then
itemsInCart
|> Map.remove productName
else
itemsInCart
|> Map.add productName { cartItem with Quantity = cartItem.Quantity-1 }
|> SET
| EventHub.CartEvent.AddOneItem productName ->
if Map.containsKey productName itemsInCart then
let cartItem = itemsInCart.[productName]
itemsInCart
|> Map.add productName { cartItem with Quantity = cartItem.Quantity+1 }
|> SET
| EventHub.CartEvent.RemoveItem productName ->
itemsInCart
|> Map.remove productName
|> SET
| EventHub.CartEvent.EmptyCart ->
Map.empty
|> SET
do updateCart ()
}
|> Async.Start
)
updateCart ()
contents
module Controls =
[]
type ShoppingCart() =
inherit Web.Control()
[]
override __.Body =
Client.ShoppingCart() :> _
[]
type ItemsToBuy() =
inherit Web.Control()
[]
override __.Body =
Client.ItemsToBuy() :> _
module Skin =
open System.Web
type Page =
{
Title : string
Items : list
ShoppingCart : list
}
let MainTemplate =
Content.Template("~/Main.html")
.With("title", fun x -> x.Title)
.With("items", fun x -> x.Items)
.With("shoppingcart", fun x -> x.ShoppingCart)
let WithTemplate title items shoppingcart : Content =
Content.WithTemplate MainTemplate <| fun context ->
{
Title = title
Items = items context
ShoppingCart = shoppingcart context
}
module Site =
let ( => ) text url =
A [HRef url] -< [Text text]
let HomePage =
Skin.WithTemplate "ShoppingCart"
<| fun ctx ->
[
Div [new Controls.ItemsToBuy()]
]
<| fun ctx ->
[
Div [new Controls.ShoppingCart()]
]
let Main =
Sitelet.Sum [
Sitelet.Content "/" Home HomePage
]
[]
type Website() =
interface IWebsite with
member this.Sitelet = Site.Main
member this.Actions = [Home]
type Global() =
inherit System.Web.HttpApplication()
member g.Application_Start(sender: obj, args: System.EventArgs) =
()
[)>]
do ()