<|start_filename|>src/admin/Ui/Layout.elm<|end_filename|>
module Admin.Ui.Layout (..) where

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Models.User exposing (User)
import ViewHelpers exposing (inRow, inGrid)
import Admin.Ui.Utils.Message exposing (message)
import Signal exposing (Address)


navigation : Props -> Html
navigation props =
    nav
        [ class "navbar navbar-default" ]
        [ div
            [ class "container-fluid" ]
            [ div
                [ class "navbar-header" ]
                [ a
                    [ class "navbar-brand" ]
                    [ text ("Monger's Place : " ++ props.user.username) ]
                ]
            , p
                [ class "navbar-text navbar-right"
                , style [ ( "margin-right", "0" ) ]
                ]
                [ button
                    [ class "navbarButton"
                    , onClick props.onLogout () ]
                    [ text "Log Out" ]
                ]
            ]
        ]


type alias Props =
    { user : User
    , flash : String
    , error : String
    , onLogout : Address ()
    }


wrap : Props -> Html -> Html
wrap props children =
    div
        [ class "admin layout" ]
        [ navigation props
        , inGrid
            << inRow
            <| div
                []
                [ div
                    [ class "flash" ]
                    [ message
                        "success"
                        (props.flash /= "")
                        "flash"
                        [ text props.flash ]
                    ]
                , if props.error /= "" then
                    div [ class "alert alert-danger" ] [ text props.error ]
                  else
                    div [] []
                , children
                ]
        ]


<|start_filename|>src/Ui/Tastings.elm<|end_filename|>
module Ui.Tastings (..) where

import Html exposing (..)
import Html.Attributes exposing (..)
import Ui.ProductPage exposing (productPage)
import Models.Product exposing (Product)


type alias Props =
    { products : List Product
    }


subheader =
    div
        []
        [ p [] [ text "Cheese tastings are blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah" ]
        , p [ class "attention" ] [ text "If you want to make specific cheese selections, or you have more than 14 people, please contact us directly for arrangements." ]
        ]


view : Props -> Html
view props =
    productPage
        { products = props.products
        , category = "Tastings"
        , header = "Cheese Tastings"
        , subheader = subheader
        , pageClass = "pageTastings"
        }


<|start_filename|>src/vendor/elm-history/examples/ClickCounter.elm<|end_filename|>
import History
import Graphics.Element exposing (Element, show)
import Mouse
import Signal exposing (Signal)
import Task exposing (Task)

makeTitle n = "n = " ++ toString n

port title : Signal String
port title = Signal.map makeTitle counts

makeHash n = "#" ++ toString n

port runTask : Signal (Task error ())
port runTask = Signal.map History.setPath (Signal.map makeHash counts)

update : () -> Int -> Int
update () n = n + 1

counts : Signal Int
counts = Signal.foldp update 0 Mouse.clicks

main : Signal Element
main = Signal.map show counts


<|start_filename|>src/Router.elm<|end_filename|>
module Router (..) where

import Routes
import Signal exposing (Address, forwardTo)
import Html exposing (..)
import Model exposing (Model)
import Ui.Login as Login
import Admin.Ui.Products as Products
import Admin.Ui.EditProduct as EditProduct
import Admin.Ui.Layout as AdminLayout
import Admin.Actions as AdminActions
import Admin.Model as AdminModel
import Ui.Home as Home
import Ui.Cheeses
import Ui.Baskets
import Actions exposing (Action(UpdateCreds, SubmitLogin, ShowError, LogOut))
import Ui.Layout exposing (layout)
import Ui.Platters
import Ui.Tastings


checkLoggedIn : Address Action -> Model -> Html -> Html
checkLoggedIn address model child =
    if model.user.username == "" then
        Login.view
            { creds = model.creds
            , onChange = (forwardTo address UpdateCreds)
            , onSubmit = (forwardTo address SubmitLogin)
            , onError = (forwardTo address ShowError)
            , error = model.error
            }
    else
        child


adminAction : Address Action -> (a -> AdminActions.Action) -> Address a
adminAction address adminAction =
    forwardTo address (Actions.Admin << adminAction)


products : Address Action -> Model.Model -> Html
products address model =
    Products.view
        { user = model.user
        , onEdit = adminAction address AdminActions.EditProduct
        , products = model.products
        , onNew = adminAction address (\_ -> AdminActions.NewProduct)
        }


route : Address Action -> Model -> Html
route address model =
    let
        adminLayout =
            AdminLayout.wrap
                { user = model.user
                , flash = model.admin.flash
                , error = model.admin.error
                , onLogout = (forwardTo address (\_ -> LogOut))
                }
    in
        case model.activeRoute of
            Routes.Home ->
                layout (Home.view { rotator = model.rotator })

            Routes.NotFound ->
                div [] [ text "404, not found!" ]

            Routes.Products ->
                checkLoggedIn address model
                    <| adminLayout (products address model)

            Routes.EditProduct id ->
                adminLayout
                    <| EditProduct.view
                        { title = "Edit Product"
                        , update =
                            adminAction address AdminActions.UpdateEditingProduct
                        , save =
                            adminAction address AdminActions.SaveProduct
                        , cancel =
                            adminAction address (\_ -> AdminActions.CancelEditingProduct)
                        , error = model.admin.error
                        , model = model.admin.editingProduct
                        }

            Routes.NewProduct ->
                adminLayout
                    <| EditProduct.view
                        { title = "New Product"
                        , update =
                            adminAction address AdminActions.UpdateEditingProduct
                        , save =
                            adminAction address AdminActions.SaveNewProduct
                        , cancel =
                            adminAction address (\_ -> AdminActions.CancelEditingProduct)
                        , error = model.admin.error
                        , model = model.admin.editingProduct
                        }

            Routes.Cheeses ->
                layout (Ui.Cheeses.view { products = model.products })

            Routes.Baskets ->
                layout (Ui.Baskets.view { products = model.products })

            Routes.Platters ->
                layout (Ui.Platters.view { products = model.products })

            Routes.Tastings ->
                layout (Ui.Tastings.view { products = model.products })


<|start_filename|>src/ViewHelpers.elm<|end_filename|>
module ViewHelpers where

import Html exposing (..)
import Html.Attributes exposing (class)

inRow : Html -> Html
inRow el =
  div
    [class "row"]
    [ div
      [class "col-sm-12"]
      [el]
    ]

inGrid : Html -> Html
inGrid el =
  div
    [class "container"]
    [el]


<|start_filename|>src/Ui/Cheeses.elm<|end_filename|>
module Ui.Cheeses (..) where

import Html exposing (..)
import Html.Attributes exposing (..)
import ViewHelpers exposing (inGrid, inRow)
import Models.Product exposing (Product)
import List exposing (map, filter, sortBy)
import List.Extra exposing (dropDuplicates)
import Ui.PageHeader exposing (pageHeader)


headerBox =
    div
        [ class "box" ]
        [ h1
            []
            [ text "Cheeses" ]
        , p
            []
            [ text "" ]
        ]


type alias Props =
    { products : List Product
    }


variants : List Product -> List String
variants ps =
    dropDuplicates
        <| map .variant
        <| filter (\p -> p.category == "Cheeses") ps


getVariant : String -> List Product -> List Product
getVariant variant ps =
    sortBy .name
        <| filter (\p -> p.variant == variant) ps


renderCheese cheese =
    li
        [ class "cheese row" ]
        [ div
            [ class "col-xs-12" ]
            [ div
                [ class "thumbnail col-sm-3"
                , style [ ( "background-image", "url(" ++ cheese.image ++ ")" ) ]
                ]
                []
            , div
                [ class "col-sm-9" ]
                [ h3 [] [ text cheese.name ]
                , span [ class "description" ] [ text cheese.description ]
                , div [ class "clearfix" ] []
                ]
            ]
        ]


renderVariant : List Product -> String -> Html
renderVariant ps variant =
    div
        [ class "variant box" ]
        [ h2 [] [ text variant ]
        , ul
            [ class "cheeses" ]
            (map renderCheese (getVariant variant ps))
        ]


view : Props -> Html
view props =
    div
        [ class "pageCheeses showcase" ]
        [ pageHeader "Cheeses" (div [] [ text "We source fine cheeses from all over the world. We love to have fun with it. Volunteering your time; it pays you and your whole community fantastic dividends. It's important to me that you're happy. That's what painting is all about. It should make you feel good when you paint." ])
        , inGrid
            << inRow
            <| ul
                [ class "variantList" ]
                (map
                    (renderVariant props.products)
                    (variants props.products)
                )
        ]


<|start_filename|>src/Models/User.elm<|end_filename|>
module Models.User (..) where

type alias Creds =
  { username : String
  , password : String
  }

type alias User =
  { username: String
  , token : String
  }


<|start_filename|>src/index.js<|end_filename|>
require('./index.html')
require('./index.css')
var Elm = require('./Main.elm')

document.addEventListener("DOMContentLoaded", function() {
  var app =
  Elm.embed(
    Elm.Main,
    document.getElementById("root"),
    {
      userFromStorage: JSON.parse(
        localStorage.getItem("user")) || {username: "", token: ""},
        initialPath: window.location.hash
      }
    )


    app.ports.saveUser.subscribe(function(user) {
      localStorage.setItem("user", JSON.stringify(user))
    })
  })


<|start_filename|>src/Ui/PageHeader.elm<|end_filename|>
module Ui.PageHeader (..) where

import Html exposing (..)
import Html.Attributes exposing (..)
import ViewHelpers exposing (inGrid, inRow)


pageHeader : String -> Html -> Html
pageHeader title subtitle =
    div
        []
        [ div [ class "hero" ] []
        , div
            [ class "box" ]
            [ inGrid
                << inRow
                <| div
                    []
                    [ h1
                        []
                        [ text title ]
                    , subtitle
                    ]
            ]
        ]


<|start_filename|>src/Ui/ProductPage.elm<|end_filename|>
module Ui.ProductPage (..) where

import Html exposing (..)
import Html.Attributes exposing (..)
import ViewHelpers exposing (inGrid, inRow)
import Models.Product exposing (Product, sort)
import List exposing (map, filter, sortBy)
import List.Extra exposing (dropDuplicates)
import Ui.PageHeader exposing (pageHeader)


type alias Props =
    { products : List Product
    , category : String
    , header : String
    , subheader : Html
    , pageClass : String
    }


renderProduct product =
    li
        [ class "product box row" ]
        [ div
            [ class "thumbnail col-xs-12 col-sm-3"
            , style [ ( "background-image", "url(" ++ product.image ++ ")" ) ]
            ]
            []
        , div
            [ class "col-xs-12 col-sm-9" ]
            [ h2 [] [ text product.name ]
            , div [ class "description" ] [ text product.description ]
            ]
        ]


productPage : Props -> Html
productPage props =
    div
        [ class ("showcase " ++ props.pageClass) ]
        [ pageHeader props.header props.subheader
        , inGrid
            << inRow
            <| ul
                [ class "productList" ]
                (map
                    renderProduct
                    (filter (\p -> p.category == props.category) (sort props.products))
                )
        ]


<|start_filename|>src/Model.elm<|end_filename|>
module Model (..) where

import Effects exposing (Effects)
import Admin.Model
import Models.User exposing (User, Creds)
import Actions exposing (..)
import Routes exposing (Route)
import Utils exposing (changePath)
import Models.Product exposing (Product)
import Helpers exposing (effectify, actionify)
import Db


type alias Model =
    { admin : Admin.Model.Model
    , activeRoute : Route
    , rotator : String
    , products : List Product
    , user : User
    , creds : Creds
    , error : String
    }


init : String -> User -> ( Model, Effects Action )
init initialPath initialUser =
    let
        adminInit = Admin.Model.init initialPath
    in
        ( { admin = Admin.Model.onlyModel adminInit
          , activeRoute = Routes.getRoute initialPath
          , rotator = "Utah"
          , products = []
          , user = initialUser
          , creds = { username = "", password = "" }
          , error = ""
          }
        , Effects.map Admin (Admin.Model.onlyEffects adminInit)
        )


update : Action -> Model -> ( Model, Effects Action )
update action model =
    case action of
        NoOp ->
            ( model, Effects.none )

        GoToPath newPath ->
            ( model
            , changePath newPath
            )

        ReactToPathChange newPath ->
            ( { model | activeRoute = Routes.getRoute newPath }, Effects.none )

        -- Pages!
        Admin subAction ->
            -- TODO: Refactor so that products are only kept at the root, instead
            --       of both the root and inside of admin.
            let
                updated =
                    Admin.Model.update
                        { activeRoute = model.activeRoute, user = model.user }
                        subAction
                        model.admin

                products = (fst updated).products
            in
                -- Hacky, I'm making the action from the admin's products request update
                -- the root products. Refactor this later.
                ( { model | admin = (fst updated), products = products }
                , Effects.map Actions.Admin (snd updated)
                )

        UpdateRotator v ->
            ( { model | rotator = v }
            , Effects.none
            )

        UpdateCreds creds ->
            ( { model | creds = creds }
            , Effects.none
            )

        UpdateUser user ->
            ( { model | user = user }
            , Effects.none
            )

        SubmitLogin creds ->
            ( model
            , effectify
                <| actionify UpdateUser ShowError
                <| Db.auth creds.username creds.password
            )

        LogOut ->
            ( { model | user = { username = "", token = "" } }
            , Effects.none
            )

        ShowError err ->
            ( { model | error = err }
            , Effects.none
            )


<|start_filename|>static/admin.css<|end_filename|>
.layout {
  padding-bottom: 2em;
}

.loginWrapper {
  margin: 0 auto;
  margin-top: 6em;
  max-width: 350px;
}

.loginWrapper label {
  display: block;
}

.loginWrapper input {
  margin-bottom: 2em;
}

.loginWrapper h1 {
  margin-bottom: 2rem;
}

.loginWrapper .errorMessage {
  margin-top: 2rem;
  color: red;
}

.navbar {
  border-radius: 0 !important;
}

.adminProducts .productHeader {
  margin-bottom: 1em;
}

.adminProducts .productHeader h2 {
  float: left;
  margin: 0;
  padding: 0;
}

.adminProducts .productHeader button {
  float: right;
}

.adminProducts .productList {
  padding: 0;
  margin: 0;
}

.adminProducts .product {
  list-style: none;
  border: 1px solid #EEE;
  border-radius: .4rem;
  box-shadow: 0px 5px 10px rgba(0, 0, 0, .1);
  padding: 1em;
  padding-right: 10em;
  position: relative;
  background: white;
}

.adminProducts .product + .product {
  margin-top: 2em;
}

.adminProducts .products .name {
  font-size: 2em;
  color: #222;
}

.adminProducts .products .description {
  margin-bottom: .5em;
}

.adminProducts .products .buttonBar {
  margin-top: 1em;
}

.adminProducts .products .buttonBar button + button {
  margin-left: .5em;
}

.adminProducts .products .thumb {
  background-size: cover;
  background-position: center;
  width: 10em;
  border-radius: 0 .4rem .4rem 0;
  position: absolute;
  right: 0;
  top: 0;
  height: 100%;
}

.editProduct label {
  margin-top: 1em;
}

.editProduct .buttonRow {
  margin-top: 2em;
  text-align: right;
  margin-bottom: 2em;
}

.editProduct .buttonRow .btn {
  min-width: 10em;
}

.editProduct .buttonRow .btn + .btn {
  margin-left: 1em;
}

.editProduct .thumbPreview {
  width: 200px;
  height: 200px;
  border-radius: 3px;
  margin: 0 auto;
  margin-top: 1em;
  background-size: cover;
  background-position: center;
  background-color: hsla(210,29%,80%,1);
}

.products .alert.msg-show {
  transition: 300ms;
}

.products .alert.msg-hide {
  padding-top: 0;
  padding-bottom: 0;
  margin: 0;
  transition: 300ms;
  height: 0;
  opacity: 0;
}

.flash {
  position: fixed;
  bottom: 0;
  width: 50vw;
  right: 1em;
  z-index: 100000;
}

.flash .alert.msg-show {
  transition: 300ms;
}

.flash .alert.msg-hide {
  transition: 300ms;
  opacity: 0;
  transform: translateY(-5px);
}

.admin.layout .navbar-brand {
  margin-top: 13px;
  height: auto;
}


<|start_filename|>src/Ui/Login.elm<|end_filename|>
module Ui.Login (..) where

import Html exposing (..)
import Html.Attributes exposing (class)
import Html.Events exposing (onClick)
import Signal exposing (Address)
import FormHelpers exposing (textInput, passwordInput)
import ViewHelpers exposing (inRow, inGrid)
import Models.User exposing (User, Creds)


type alias Props =
    { creds : Creds
    , onChange : Address Creds
    , onSubmit : Address Creds
    , onError : Address String
    , error : String
    }


view : Props -> Html
view props =
    let
        creds = props.creds

        upName = (\name -> { creds | username = name })

        upPass = (\pass -> { creds | password = <PASSWORD> })
    in
        inGrid
            << inRow
            <| div
                [ class "loginWrapper" ]
                [ h1 [] [ text "Login" ]
                , label [] [ text "Username" ]
                , textInput props.onChange props.creds.username upName
                , label [] [ text "Password" ]
                , passwordInput props.onChange props.creds.password upPass
                , button
                    [ class "btn btn-primary"
                    , onClick props.onSubmit props.creds
                    ]
                    [ text "Submit" ]
                , div [ class "errorMessage" ] [ text props.error ]
                ]



-- Effects
-- submitLogin : Props -> User -> Effects Action
-- submitLogin props u =
--     Db.auth u.username u.password
--         |> Task.toResult
--         |> Task.map
--             (\res ->
--                 case res of
--                     Ok user ->
--                         SetUser user
--
--                     Err err ->
--                         ShowMessage err
--             )
--
--
-- redirectToHome : Effects Action
-- redirectToHome =
--     History.setPath "/products"
--         |> Task.map (always NoOp)
--         |> Effects.task


<|start_filename|>src/admin/Ui/Utils/Message.elm<|end_filename|>
module Admin.Ui.Utils.Message where

import Html exposing (..)
import Html.Attributes exposing (..)


message : String -> Bool -> String -> List Html -> Html
message color showing key children =
  let
    showClass =
      if showing then
        "show"
      else
        "hide"
  in
    div
      [ class ("alert alert-"
                ++ color
                ++ " msg-" ++ showClass)
      , Html.Attributes.key key
      ]
      children


<|start_filename|>src/Ui/Platters.elm<|end_filename|>
module Ui.Platters (..) where

import Html exposing (..)
import Html.Attributes exposing (..)
import Ui.ProductPage exposing (productPage)
import Models.Product exposing (Product)


type alias Props =
    { products : List Product
    }


subheader =
    div
        []
        [ p [] [ text "Platters are blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah" ]
        , p [ class "attention" ] [ text "If you want to make specific cheese selections, or you have more than 22 people, please contact us directly for arrangements." ]
        ]


view : Props -> Html
view props =
    productPage
        { products = props.products
        , category = "Platters"
        , header = "Platters"
        , subheader = subheader
        , pageClass = "pagePlatters"
        }


<|start_filename|>src/index.css<|end_filename|>
/** Your theme modifications here **/

* {
  box-sizing: border-box;
}

ul {
  padding: 0;
  margin: 0;
}

.attention {
  font-weight: 700;
}

li {
  list-style: none;
}

.clearfix {
  clear: both;
}

.brandLogoLarge {
    width: 139px;
    height: 50px;
}

.brandLogoSmall {
    width: 93px;
    height:60px;
}

.navbarButton {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
}

.hero {
    height: 450px;
    background-size: cover;
    background-position: center;
    margin-top: -30px;
}

.pageHome .hero {
    background-image: url(/img/hero-2.jpg);
}

.pageBaskets .hero {
    background-image: url(/img/basket-lg.jpg);
}

.pageCheeses .hero {
    background-image: url(/img/hero-cheeses.png);
}

.pagePlatters .hero {
    background-image: url(/img/hero-platters.png);
}

.pageTastings .hero {
    background-image: url(/img/hero-tastings.png);
}

@media (max-width: 570px) {
  .pageHome .rotator {
    font-size: 5.5vw !important;
  }

  .pageHome .rotatorWrapper {
    padding-left: 0;
    padding-right: 0;
  }
}

.pageHome .rotator {
  font-weight: 100;
  font-size: 2.5rem;
}

.pageHome .rotator .leftHalf {
  display: inline-block;
  text-align: left;
  padding: .15em;
}

.pageHome .rotator .rightHalf {
  display: inline-block;
  animation: fade 3s infinite;
  padding: .15em;
}

@keyframes fade {
  from {
    opacity: 0;
  }

  15% {
    opacity: 1;
  }

  85% {
    opacity: 1;
  }

  to {
    opacity: 0;
  }
}

@media (min-width: 768px) {
  .pageHome .features .box {
    margin-bottom: 0;
  }

  .pageHome .features {
    margin-top: 10em;
    margin-bottom: 10em;
  }
}

.pageHome .featureThumb {
  background-size: cover;
  background-position: center;
  height: 300px;
  width: 100%;
}

@media (min-width: 768px) {
  .pageHome .featureThumb {
    height: 200px;
    width: 100%;
  }
}

#footer {
  margin-top: 4rem;
}

#footer h1 {
  font-size: 2rem;
  font-weight: 100;
  margin-bottom: .25em;
}

.showcase .thumbnail {
  min-height: 150px;
  background-size: cover;
  background-position: center;
}

@media (min-width: 571px) {
  .showcase .thumbnail {
    min-height: 400px;
  }
}

@media (min-width: 768px) {
  .showcase .thumbnail {
    margin: 0;
    min-height: 150px;
  }
}

@media (min-width: 992px) {
  .showcase .thumbnail {
    min-height: 170px;
  }
}

.showcase .description {
  color: #888;
  font-size: 1.2em;
  font-weight: 100;
}

.showcase .price {
  font-size: 2em;
  font-weight: 700;
  margin-right: .5em;
  display: inline-block;
  transform: translateY(7px);
}

.showcase .priceRow {
  margin-top: .5em;
  text-align: right;
}

.pageCheeses .cheese {
  margin-top: 1.5em;
}

.pageCheeses h3 {
  margin-top: 0;
  margin-bottom: .5rem;
}

.pageCheeses h2 {
  margin-bottom: 2rem;
}

.pageBaskets h2 {
  margin-top: 0;
  margin-bottom: .5rem;
  font-size: 1.8em;
}

.bigLogo {
  max-width: 150px;
  display: block;
  margin: 2em auto;
}

#copyright a {
  color: #FFE494;
}

#copyright {
  text-align: center;
}


<|start_filename|>src/Actions.elm<|end_filename|>
module Actions (..) where

import Admin.Actions
import Models.User exposing (Creds, User)


type Action
    = ReactToPathChange String
    | GoToPath String
    | NoOp
    | Admin Admin.Actions.Action
    | UpdateRotator String
    | UpdateCreds Creds
    | UpdateUser User
    | SubmitLogin Creds
    | LogOut
    | ShowError String


<|start_filename|>src/admin/Ui/Products.elm<|end_filename|>
module Admin.Ui.Products (..) where

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Signal
import Models.User exposing (User)
import Models.Product exposing (Product, getAllProducts, emptyProduct, sort)
import Admin.Ui.Utils.Message exposing (message)


thumbnail : String -> Html
thumbnail url =
    div [ style [ ( "background-image", "url(" ++ url ++ ")" ) ], class "thumb" ] []


renderProduct : Props -> Product -> Html
renderProduct props prod =
    let
        price =
            case prod.price of
                "" ->
                    text ""

                _ ->
                    text ("Price: $" ++ prod.price)
    in
        li
            [ class "product" ]
            [ thumbnail prod.image
            , div
                [ class "info" ]
                [ div [ class "name" ] [ text prod.name ]
                , div [ class "description" ] [ text prod.description ]
                , div [] [ text ("Category: " ++ prod.category) ]
                , div [] [ text ("Variant: " ++ prod.variant) ]
                , div [] [ price ]
                ]
            , div
                [ class "buttonBar" ]
                [ button
                    [ class "btn btn-default btn-sm"
                    , onClick props.onEdit prod.id
                    ]
                    [ text "Edit" ]
                ]
            ]


type alias Props =
    { user : User
    , onEdit : Signal.Address String
    , onNew : Signal.Address ()
    , products : List Product
    }


view : Props -> Html
view props =
    div
        [ class "adminProducts" ]
        [ div
            []
            [ div
                [ class "section products" ]
                [ div
                    [ class "productHeader" ]
                    [ h2 [] [ text "Products" ]
                    , button
                        [ class "btn btn-success"
                        , onClick props.onNew ()
                        ]
                        [ text "New Product" ]
                    , div [ class "clearfix" ] []
                    ]
                , ul
                    [ class "productList" ]
                    (List.map (renderProduct props) (sort props.products))
                ]
            ]
        ]


<|start_filename|>src/admin/Actions.elm<|end_filename|>
module Admin.Actions (..) where

import Models.Product exposing (Product)


type Action
    = EditProduct String
    | UpdateEditingProduct Product
    | FetchProductsFin (List Product)
    | SaveProduct Product
    | SaveProductFin Product
    | CancelEditingProduct
    | NewProduct
    | SaveNewProduct Product
    | DisplayError String
    | ClearFlash
    | NoOp


<|start_filename|>src/Ui/Baskets.elm<|end_filename|>
module Ui.Baskets (..) where

import Html exposing (..)
import Html.Attributes exposing (..)
import ViewHelpers exposing (inGrid, inRow)
import Models.Product exposing (Product)
import List exposing (map, filter, sortBy)
import List.Extra exposing (dropDuplicates)
import Ui.PageHeader exposing (pageHeader)
import Ui.ProductPage exposing (productPage)


type alias Props =
    { products : List Product
    }


subheader =
    div
        []
        [ p [] [ text "Baskets are blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah" ]
        ]


view : Props -> Html
view props =
    productPage
        { products = props.products
        , category = "Baskets"
        , header = "Baskets"
        , subheader = subheader
        , pageClass = "pageBaskets"
        }


<|start_filename|>src/Ui/Rotator.elm<|end_filename|>
module Ui.Rotator (..) where

import Html exposing (..)
import Html.Attributes exposing (..)
import Maybe
import Array
import Time
import Signal exposing (Signal, foldp)


type alias Props =
    { left : String
    , right : String
    }


view : Props -> Html
view props =
    div
        [ class "rotator" ]
        [ div
            [ class "leftHalf" ]
            [ text props.left ]
        , div
            [ class "rightHalf" ]
            [ text props.right ]
        ]


indexToName : List String -> Int -> String
indexToName xs i =
    Maybe.withDefault "bad index"
        <| Array.get i
        <| Array.fromList xs


changes : List String -> Signal String
changes orig =
    foldp
        (\_ idx -> (idx + 1) % (List.length orig))
        0
        (Time.every (Time.second * 3))
        |> Signal.map (indexToName orig)


<|start_filename|>src/Ui/Home.elm<|end_filename|>
module Ui.Home (..) where

import Html exposing (..)
import Html.Attributes exposing (..)
import Ui.Rotator


heroImage : Html
heroImage =
    div
        [ class "hero" ]
        []


type alias Props =
    { rotator : String
    }


rotator props =
    div
        [ class "box" ]
        [ div
            [ class "container" ]
            [ div
                [ class "row" ]
                [ div
                    [ class "col-md-12" ]
                    [ h1
                        []
                        [ Ui.Rotator.view
                            { left = "Fine cheeses from", right = props.rotator }
                        ]
                    ]
                ]
            ]
        ]


featureBox title content bg =
    div
        [ class "col-sm-4" ]
        [ div
            [ style [ ( "background-image", "url(" ++ bg ++ ")" ) ]
            , class "featureThumb"
            ]
            []
        , div
            [ class "box same-height clickable"
            , style
                [ ( "height", "auto" )
                ]
            ]
            [ h3
                []
                [ a
                    [ href "#" ]
                    [ text title ]
                ]
            , p
                []
                content
            ]
        ]


features =
    div
        [ class "features" ]
        [ div
            [ class "container" ]
            [ div
                [ class "same-height-row row" ]
                [ featureBox "Platters" [ text "Gotch'er platters here!" ] "/img/feature-1.jpg"
                , featureBox "Baskets" [ text "Gotch'er baskets here!" ] "/img/feature-2.jpg"
                , featureBox "Tastings" [ text "Want a taste? Yum!" ] "/img/feature-3.jpg"
                ]
            ]
        ]


view : Props -> Html
view props =
    div
        [ class "pageHome" ]
        [ heroImage
        , rotator props
        , features
        ]


<|start_filename|>src/Ui/TopBar.elm<|end_filename|>
module Ui.TopBar (..) where

import Html exposing (..)
import Html.Attributes exposing (..)


view : Html
view =
    div
        [ class "navbar navbar-default yamm"
        , id "navbar"
        , attribute "role" "navigation"
        ]
        [ div
            [ class "container" ]
            [ div
                [ class "navbar-header" ]
                [ a
                    [ class "navbar-brand home"
                    , attribute "data-animate-hover" "bounce"
                    , href "#/"
                    ]
                    [ img
                        [ alt "Cheese Palette logo"
                        , class "hidden-xs brandLogoLarge"
                        , src "/img/logo.svg"
                        ]
                        []
                    , img
                        [ alt "Cheese Palette logo", class "visible-xs brandLogoSmall", src "/img/logo.svg" ]
                        []
                    , span
                        [ class "sr-only" ]
                        [ text "Cheese Palette go to homepage" ]
                    ]
                , div
                    [ class "navbar-buttons" ]
                    [ button
                        [ class "navbar-toggle", attribute "data-target" "#navigation", attribute "data-toggle" "collapse", type' "button" ]
                        [ span
                            [ class "sr-only" ]
                            [ text "Toggle navigation" ]
                        , i
                            [ class "fa fa-align-justify" ]
                            []
                        ]
                    ]
                ]
            , div
                [ class "navbar-collapse collapse", id "navigation" ]
                [ ul
                    [ class "nav navbar-nav navbar-left" ]
                    [ li
                        []
                        [ a
                            [ href "#/cheeses" ]
                            [ text "Cheeses" ]
                        ]
                    , li
                        []
                        [ a
                            [ href "#/baskets" ]
                            [ text "Baskets" ]
                        ]
                    , li
                        []
                        [ a
                            [ href "#/platters" ]
                            [ text "Platters" ]
                        ]
                    , li
                        []
                        [ a
                            [ href "#/tastings" ]
                            [ text "Tastings" ]
                        ]
                    ]
                ]
            ]
        ]


<|start_filename|>src/Models/Product.elm<|end_filename|>
module Models.Product (..) where

import Json.Encode as Encoder exposing (encode)
import Json.Decode as Decoder exposing ((:=))
import Json.Decode.Extra exposing ((|:), withDefault)
import Task exposing (Task, andThen)
import Http exposing (send, defaultSettings, fromJson)
import Effects exposing (Effects)
import Db
import Dict


type alias Product =
    { id : String
    , name : String
    , description : String
    , image : String
    , category : String
    , variant : String
    , price : String
    , sortIndex : Int
    }


emptyProduct : Product
emptyProduct =
    { id = ""
    , name = ""
    , description = ""
    , image = ""
    , category = ""
    , variant = ""
    , price = ""
    , sortIndex = 0
    }


decoder : Decoder.Decoder Product
decoder =
    Decoder.succeed
        Product
        |: ("id" := Decoder.string)
        |: ("name" := Decoder.string)
        |: ("description" := Decoder.string)
        |: ("image" := Decoder.string)
        |: ("category" := Decoder.string)
        |: ("variant" := Decoder.string)
        |: ("price" := Decoder.string)
        |: withDefault 0 ("sortIndex" := Decoder.int)


encoder : Product -> Encoder.Value
encoder p =
    Encoder.object
        [ ( "id", (Encoder.string p.id) )
        , ( "name", (Encoder.string p.name) )
        , ( "description", (Encoder.string p.description) )
        , ( "image", (Encoder.string p.image) )
        , ( "category", (Encoder.string p.category) )
        , ( "variant", (Encoder.string p.variant) )
        , ( "price", (Encoder.string p.price) )
        , ( "sortIndex", (Encoder.int p.sortIndex) )
        ]


encodeProduct : Product -> String
encodeProduct p =
    encode 2 (encoder p)


sort : List Product -> List Product
sort products =
    List.sortWith
        (\a b ->
            if a.category /= b.category then
                compare a.category b.category
            else if a.sortIndex /= b.sortIndex then
                compare a.sortIndex b.sortIndex
            else if a.variant /= b.variant then
                compare a.variant b.variant
            else
                compare a.name b.name
        )
        products


save : String -> Product -> Task Http.Error Product
save token prod =
    send
        defaultSettings
        { verb = "PUT"
        , headers = [ ( "Content-Type", "application/json" ) ]
        , url = (Db.productUrl prod.id) ++ "?auth=" ++ token
        , body = Http.string (encodeProduct prod)
        }
        |> fromJson decoder


saveProduct : String -> Product -> (Product -> a) -> (String -> a) -> Effects a
saveProduct token prod successAction failureAction =
    Effects.task
        (Task.toResult (save token prod)
            |> Task.map
                (\result ->
                    case result of
                        Ok saved ->
                            successAction saved

                        Err err ->
                            failureAction (toString err)
                )
        )


newProduct : String -> Product -> (Product -> a) -> (String -> a) -> Effects a
newProduct token prod successAction failureAction =
    Effects.task
        <| ((send
                defaultSettings
                { verb = "POST"
                , headers = [ ( "Content-Type", "application/json" ) ]
                , url = Db.productsUrl ++ "?auth=" ++ token
                , body = Http.string (encodeProduct prod)
                }
                |> fromJson ("name" := Decoder.string)
            )
                `andThen` (\newId -> save token { prod | id = newId })
                |> Task.toResult
                |> Task.map
                    (\result ->
                        case result of
                            Ok saved ->
                                successAction saved

                            Err err ->
                                failureAction (toString err)
                    )
           )


getAllProducts : (List Product -> a) -> (String -> a) -> Effects a
getAllProducts successAction failureAction =
    Http.get (Decoder.dict decoder) Db.productsUrl
        |> Task.toResult
        |> Task.map
            (\result ->
                case result of
                    Ok prods ->
                        successAction (Dict.values prods)

                    Err err ->
                        failureAction (toString err)
            )
        |> Effects.task


<|start_filename|>src/Ui/Layout.elm<|end_filename|>
module Ui.Layout (..) where

import Html exposing (..)
import Html.Attributes exposing (..)
import Ui.TopBar
import String
import Char


unicode : Int -> String
unicode i =
    String.fromChar (Char.fromCode i)


copyright : Html
copyright =
    div
        [ id "copyright" ]
        [ div
            [ class "container" ]
            [ div
                [ class "col-xs-12" ]
                [ p
                    []
                    [ text ((unicode 169) ++ " 2015 The Cheese Palette.")
                    , img [ src "/img/big-logo.svg", class "bigLogo" ] []
                    ]
                ]
            , div
                [ class "col-xs-12" ]
                [ p
                    []
                    [ text "Template by "
                    , a
                        [ href "http://bootstrapious.com/e-commerce-templates" ]
                        [ text "Bootstrap Ecommerce Templates" ]
                    , text " with support from "
                    , a
                        [ href "http://kakusei.cz" ]
                        [ text ("Designov" ++ (unicode 233) ++ " p" ++ (unicode 345) ++ "edm" ++ (unicode 283) ++ "ty") ]
                    ]
                ]
            ]
        ]


footerColumn : List Html -> Html
footerColumn children =
    div [ class "col-sm-4" ] children


pages : List Html
pages =
    [ h1
        []
        [ text "Pages" ]
    , ul
        []
        [ li
            []
            [ a
                [ href "/cheeses" ]
                [ text "Cheeses" ]
            ]
        , li
            []
            [ a
                [ href "/baskets" ]
                [ text "Baskets" ]
            ]
        , li
            []
            [ a
                [ href "/platters" ]
                [ text "Platters" ]
            ]
        , li
            []
            [ a
                [ href "/tastings" ]
                [ text "Tastings" ]
            ]
        ]
    ]


contact : List Html
contact =
    [ h1
        []
        [ text "Contact Us" ]
    , p
        []
        [ strong
            []
            [ text "The Cheese Palette" ]
        , br [] []
        , text "1058 Lynnwood Dr."
        , br [] []
        , text "Orem, UT"
        , br [] []
        , text "84043"
        , br [] []
        , strong
            []
            [ a
                [ href "tel:801-999-0608" ]
                [ text "(801) 999-0608" ]
            ]
        , br [] []
        , strong
            []
            [ a
                [ href "mailto:<EMAIL>" ]
                [ text "<EMAIL>" ]
            ]
        ]
    ]


social : List Html
social =
    [ h1 [] [ text "Stay in touch" ]
    , p
        [ class "social" ]
        [ a
            [ class "facebook external", href "https://www.facebook.com/cheesepalette/?fref=ts" ]
            [ i [ class "fa fa-facebook" ] [] ]
        , a
            [ class "twitter external", href "https://twitter.com/cheese_palette" ]
            [ i [ class "fa fa-twitter" ] [] ]
        , a
            [ class "instagram external", href "https://www.instagram.com/cheese_palette/" ]
            [ i [ class "fa fa-instagram" ] [] ]
        , a
            [ class "email external", href "mailto:<EMAIL>" ]
            [ i [ class "fa fa-envelope" ] [] ]
        ]
    ]


footer : Html
footer =
    div
        [ id "footer" ]
        [ div
            [ class "container" ]
            [ div
                [ class "row" ]
                [ footerColumn pages
                , footerColumn contact
                , footerColumn social
                ]
            ]
        ]


layout : Html -> Html
layout child =
    div
        []
        [ Ui.TopBar.view
        , child
        , footer
        , copyright
        ]


<|start_filename|>src/Helpers.elm<|end_filename|>
module Helpers (..) where

import Task exposing (Task)
import Effects exposing (Effects)


actionify : (thing -> action) -> (String -> action) -> Task error thing -> Task nothing action
actionify onSuccess onError task =
    Task.toResult task
        |> Task.map
            (\res ->
                case res of
                    Ok thing ->
                        onSuccess thing

                    Err error ->
                        onError (toString error)
            )


effectify : Task Effects.Never action -> Effects action
effectify task =
    Effects.task task


<|start_filename|>src/Main.elm<|end_filename|>
module Main (..) where

import StartApp as StartApp
import Task
import Effects exposing (Never, Effects)
import History
import Signal
import Html exposing (Html, text)
import Task exposing (Task)
import Model exposing (Model)
import Actions exposing (Action)
import Models.User exposing (User)
import Ui.Rotator as Rotator
import Router


view : Signal.Address Action -> Model -> Html
view address model =
    Router.route address model


routeChanges : Signal Action
routeChanges =
    Signal.map Actions.ReactToPathChange History.hash


rotatorChanges : Signal Action
rotatorChanges =
    Signal.map
        Actions.UpdateRotator
        <| Rotator.changes
            [ "Utah"
            , "Finland"
            , "Ireland"
            , "Austria"
            , "New York"
            , "Perelandra"
            , "Norway"
            ]


app : StartApp.App Model
app =
    StartApp.start
        { init = Model.init initialPath userFromStorage
        , update = Model.update
        , view = view
        , inputs = [ routeChanges, rotatorChanges ]
        }


main : Signal Html
main =
    app.html


port tasks : Signal (Task.Task Never ())
port tasks =
    app.tasks



-- Local Storage


port userFromStorage : User
port initialPath : String
port saveUser : Signal User
port saveUser =
    app.model
        |> Signal.map .user
        |> Signal.dropRepeats


<|start_filename|>src/Db.elm<|end_filename|>
module Db (..) where

import Secrets exposing (apiBase)
import Task exposing (map, mapError, Task)
import Models.User exposing (User)
import ElmFire.Auth exposing (withPassword, authenticate)
import ElmFire exposing (fromUrl)
import Json.Decode as Decode exposing (decodeValue, (:=))
import Json.Encode
import Result exposing (toMaybe)
import Maybe


productsUrl : String
productsUrl =
    apiBase ++ "/products.json"


productUrl : String -> String
productUrl id =
    apiBase ++ "/products/" ++ id ++ ".json"


getEmail : Json.Encode.Value -> String
getEmail val =
    decodeValue ("email" := Decode.string) val
    |> toMaybe |> Maybe.withDefault "Unparseable email"


auth : String -> String -> Task String User
auth email pass =
    let
        loc = fromUrl apiBase
    in
        authenticate loc [] (withPassword email pass)
            |> mapError toString
            |> map
                (\deets ->
                    { username = getEmail deets.specifics
                    , token = deets.token
                    }
                )


<|start_filename|>src/Utils.elm<|end_filename|>
module Utils where

import History
import Task
import Effects exposing (Effects)
import Actions exposing (..)


changePath : String -> Effects Action
changePath newPath =
  Effects.task (Task.map (\_ -> NoOp) (History.setPath newPath))


<|start_filename|>src/admin/Model.elm<|end_filename|>
module Admin.Model (..) where

import Effects exposing (Effects)
import Admin.Actions exposing (..)
import Routes exposing (Route)
import Task exposing (Task, andThen, sleep, succeed)
import Models.Product as Product exposing (emptyProduct, Product, getAllProducts, saveProduct, newProduct)
import Utils exposing (changePath)
import Models.User exposing (User)


type alias Model =
    { editingProduct : Product.Product
    , products : List Product
    , error : String
    , pending : Bool
    , flash : String
    }


init : String -> ( Model, Effects Action )
init initialPath =
    ( { editingProduct = Product.emptyProduct
      , products = []
      , error = ""
      , pending = False
      , flash = ""
      }
    , fetchProducts
    )


updateProductInPlace : Product -> List Product -> List Product
updateProductInPlace p ps =
    let
        isInList = (List.length (List.filter (\i -> i.id == p.id) ps)) /= 0
    in
        if isInList then
            List.map
                (\i ->
                    if i.id == p.id then
                        p
                    else
                        i
                )
                ps
        else
            p :: ps


fetchProducts : Effects Action
fetchProducts =
    getAllProducts FetchProductsFin DisplayError


getProduct : String -> List Product -> Product
getProduct id products =
    Maybe.withDefault emptyProduct
        <| List.head
        <| List.filter (\p -> p.id == id) products


clearFlash : Effects Action
clearFlash =
    Effects.task
        <| sleep 2000
        `andThen` \_ -> succeed ClearFlash


sortProducts : List Product -> List Product
sortProducts =
    List.sortBy .name


type alias Props =
    { activeRoute : Route
    , user: User
    }


update : Props -> Action -> Model -> ( Model, Effects Action )
update props action model =
    case action of
        EditProduct id ->
            ( { model | editingProduct = getProduct id model.products }
            , Effects.map (always NoOp) (changePath ("/#/admin/products/edit/" ++ id))
            )

        UpdateEditingProduct prod ->
            ( { model | editingProduct = prod }
            , Effects.none
            )

        DisplayError e ->
            ( { model | error = e }
            , Effects.none
            )

        FetchProductsFin ps ->
            let
                editing =
                    case props.activeRoute of
                        Routes.EditProduct id ->
                            getProduct id ps

                        _ ->
                            model.editingProduct
            in
                ( { model | products = ps, editingProduct = editing }
                , Effects.none
                )

        SaveProduct p ->
            ( { model | pending = True, error = "" }
            , saveProduct props.user.token p SaveProductFin DisplayError
            )

        SaveProductFin p ->
            ( { model
                | products = (updateProductInPlace p model.products)
                , editingProduct = emptyProduct
                , flash = "Product saved!"
                , pending = False
              }
            , Effects.batch
                [ Effects.map (always NoOp) (changePath "/#/admin/products")
                , clearFlash
                ]
            )

        CancelEditingProduct ->
            ( { model | editingProduct = emptyProduct }
            , Effects.map (always NoOp) (changePath "/#/admin/products")
            )

        NewProduct ->
            ( { model | editingProduct = emptyProduct }
            , Effects.map (always NoOp) (changePath "/#/admin/products/new")
            )

        SaveNewProduct p ->
            ( { model | pending = True, error = "" }
            , newProduct props.user.token p SaveProductFin DisplayError
            )

        ClearFlash ->
            ( { model | flash = "" }
            , Effects.none
            )

        NoOp ->
            ( model
            , Effects.none
            )


onlyModel : ( Model, Effects Action ) -> Model
onlyModel tuple =
    let
        ( model, effects ) = tuple
    in
        model


onlyEffects : ( Model, Effects Action ) -> Effects Action
onlyEffects tuple =
    let
        ( model, effects ) = tuple
    in
        effects


<|start_filename|>src/admin/Ui/EditProduct.elm<|end_filename|>
module Admin.Ui.EditProduct (..) where

import Signal exposing (Address)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import ViewHelpers exposing (inGrid, inRow)
import FormHelpers exposing (textInput, numberInput)
import Models.Product exposing (Product)


type alias Props =
    { title : String
    , model : Product
    , update : Address Product
    , save : Address Product
    , cancel : Address ()
    , error : String
    }


setName : Product -> String -> Product
setName model name =
    { model | name = name }


setDescription : Product -> String -> Product
setDescription model description =
    { model | description = description }


setImage : Product -> String -> Product
setImage model image =
    { model | image = image }


setCategory : Product -> String -> Product
setCategory model category =
    { model | category = category }


setVariant : Product -> String -> Product
setVariant model variant =
    { model | variant = variant }


setPrice : Product -> String -> Product
setPrice model price =
    { model | price = price }


setSortIndex : Product -> Int -> Product
setSortIndex model idx =
    { model | sortIndex = idx }


view : Props -> Html
view props =
    inGrid
        <| inRow
        <| div
            [ class "editProduct" ]
            [ h1 [] [ text props.title ]
            , label [] [ text "Name" ]
            , textInput
                props.update
                props.model.name
                (setName props.model)
            , label [] [ text "Description" ]
            , textInput
                props.update
                props.model.description
                (setDescription props.model)
            , div
                []
                [ label [] [ text "Image" ]
                  -- TODO: Provide an image uploader
                , textInput
                    props.update
                    props.model.image
                    (setImage props.model)
                , div
                    [ class "thumbPreview"
                    , style [ ( "background-image", "url(" ++ props.model.image ++ ")" ) ]
                    ]
                    []
                ]
            , label [] [ text "Category" ]
            , textInput
                props.update
                props.model.category
                (setCategory props.model)
            , label [] [ text "Variant" ]
            , textInput
                props.update
                props.model.variant
                (setVariant props.model)
            , label [] [ text "Price" ]
            , textInput
                props.update
                props.model.price
                (setPrice props.model)
            , label [] [ text "Sort Index (lower numbers come first on the page)" ]
            , numberInput
                props.update
                props.model.sortIndex
                (setSortIndex props.model)
            , div
                [ class "buttonRow" ]
                [ button
                    [ class "btn btn-warning"
                    , onClick props.cancel ()
                    ]
                    [ text "Cancel" ]
                , button
                    [ class "btn btn-primary"
                    , onClick props.save props.model
                    ]
                    [ text "Save" ]
                ]
            ]


<|start_filename|>src/FormHelpers.elm<|end_filename|>
module FormHelpers (..) where

import Html exposing (..)
import Html.Attributes exposing (type', class, value)
import Html.Events exposing (on, targetValue)
import Signal
import String


boundInput : String -> Signal.Address a -> String -> (String -> a) -> Html
boundInput inputType address val setter =
    input
        [ type' inputType
        , class "form-control"
        , on "input" targetValue (Signal.message address << setter)
        , value val
        ]
        []


textInput : Signal.Address a -> String -> (String -> a) -> Html
textInput =
    boundInput "text"


numberInput : Signal.Address a -> Int -> (Int -> a) -> Html
numberInput address value setter =
    boundInput "number" address (toString value) (setter << (Result.withDefault 0 << String.toInt))


passwordInput : Signal.Address a -> String -> (String -> a) -> Html
passwordInput =
    boundInput "password"


<|start_filename|>src/Routes.elm<|end_filename|>
module Routes (..) where

import RouteParser exposing (..)


type Route
    = Home
    | Products
    | NotFound
    | NewProduct
    | EditProduct String
    | Cheeses
    | Baskets
    | Platters
    | Tastings


routeParsers : Parsers Route
routeParsers =
    [ static Home "#/"
    , static Home ""
    , static Products "#/admin/products"
    , static NewProduct "#/admin/products/new"
    , dyn1 EditProduct "#/admin/products/edit/" string ""
    , static Cheeses "#/cheeses"
    , static Baskets "#/baskets"
    , static Platters "#/platters"
    , static Tastings "#/tastings"
    ]


getRoute : String -> Route
getRoute activePath =
    let
        maybeRoute = match routeParsers activePath
    in
        case maybeRoute of
            Just r ->
                r

            Nothing ->
                NotFound
