Add new smoothie route.

This commit is contained in:
Dillon Kearns 2022-06-13 12:18:38 -07:00
parent a35f51600a
commit 7f5e4f9266
3 changed files with 290 additions and 2 deletions

View File

@ -0,0 +1,264 @@
module Route.New exposing (ActionData, Data, Model, Msg, route)
import Api.Scalar exposing (Uuid(..))
import Data.Smoothies as Smoothies
import DataSource exposing (DataSource)
import Dict
import Dict.Extra
import Effect exposing (Effect)
import ErrorPage exposing (ErrorPage)
import Head
import Head.Seo as Seo
import Html exposing (Html)
import Html.Attributes as Attr
import MySession
import Pages.Field as Field
import Pages.Form
import Pages.FormParser as FormParser
import Pages.Msg
import Pages.PageUrl exposing (PageUrl)
import Pages.Url
import Path exposing (Path)
import Request.Hasura
import Route
import RouteBuilder exposing (StatefulRoute, StatelessRoute, StaticPayload)
import Server.Request as Request
import Server.Response as Response exposing (Response)
import Server.Session as Session
import Shared
import View exposing (View)
type alias Model =
{}
type Msg
= NoOp
type alias RouteParams =
{}
route : StatefulRoute RouteParams Data ActionData Model Msg
route =
RouteBuilder.serverRender
{ head = head
, data = data
, action = action
}
|> RouteBuilder.buildWithLocalState
{ view = view
, update = update
, subscriptions = subscriptions
, init = init
}
init :
Maybe PageUrl
-> Shared.Model
-> StaticPayload Data ActionData RouteParams
-> ( Model, Effect Msg )
init maybePageUrl sharedModel static =
( {}, Effect.none )
update :
PageUrl
-> Shared.Model
-> StaticPayload Data ActionData RouteParams
-> Msg
-> Model
-> ( Model, Effect Msg )
update pageUrl sharedModel static msg model =
case msg of
NoOp ->
( model, Effect.none )
subscriptions : Maybe PageUrl -> RouteParams -> Path -> Shared.Model -> Model -> Sub Msg
subscriptions maybePageUrl routeParams path sharedModel model =
Sub.none
type alias Data =
{}
type alias ActionData =
{}
data : RouteParams -> Request.Parser (DataSource (Response Data ErrorPage))
data routeParams =
Request.succeed (DataSource.succeed (Response.render Data))
action : RouteParams -> Request.Parser (DataSource (Response ActionData ErrorPage))
action routeParams =
Request.map2 Tuple.pair
(Request.formParserResultNew form)
Request.requestTime
|> MySession.expectSessionDataOrRedirect (Session.get "userId" >> Maybe.map Uuid)
(\userId ( parsed, requestTime ) session ->
case parsed of
Ok okParsed ->
Smoothies.create okParsed
|> Request.Hasura.mutationDataSource requestTime
|> DataSource.map
(\_ ->
( session
, Route.redirectTo Route.Index
)
)
Err errors ->
DataSource.succeed
-- TODO need to render errors here
( session, Response.render {} )
)
head :
StaticPayload Data ActionData RouteParams
-> List Head.Tag
head static =
[]
form : FormParser.HtmlForm String { name : String, description : String, price : Int, imageUrl : String } Data Msg
form =
FormParser.andThenNew
(\name description price imageUrl ->
FormParser.ok
{ name = name.value
, description = description.value
, price = price.value
, imageUrl = imageUrl.value
}
)
(\info name description price imageUrl ->
let
errors field =
info.errors
|> Dict.get field.name
|> Maybe.withDefault []
errorsView field =
(if field.status == Pages.Form.Blurred || True then
field
|> errors
|> List.map (\error -> Html.li [] [ Html.text error ])
else
[]
)
|> Html.ul [ Attr.style "color" "red" ]
fieldView label field =
Html.div []
[ Html.label []
[ Html.text (label ++ " ")
, field |> FormParser.input []
]
, errorsView field
]
in
( [ Attr.style "display" "flex"
, Attr.style "flex-direction" "column"
, Attr.style "gap" "20px"
]
, [ fieldView "Name" name
, fieldView "Description" description
, fieldView "Price" price
, fieldView "Image" imageUrl
, Html.button [] [ Html.text "Create" ]
]
)
)
|> FormParser.field "name" (Field.text |> Field.required "Required")
|> FormParser.field "description" (Field.text |> Field.required "Required")
|> FormParser.field "price" (Field.int { invalid = \_ -> "Invalid int" } |> Field.required "Required")
|> FormParser.field "imageUrl" (Field.text |> Field.required "Required")
view :
Maybe PageUrl
-> Shared.Model
-> Model
-> StaticPayload Data ActionData RouteParams
-> View (Pages.Msg.Msg Msg)
view maybeUrl sharedModel model app =
let
pendingCreation : Result (FormParser.FieldErrors String) NewItem
pendingCreation =
form
|> FormParser.runNew
(app.pageFormState |> Dict.get "test" |> Maybe.withDefault Dict.empty)
|> .result
|> parseIgnoreErrors
in
{ title = "New Item"
, body =
[ Html.h2 [] [ Html.text "New item" ]
, FormParser.renderHtml app form
, pendingCreation
|> Debug.log "pendingCreation"
|> Result.toMaybe
|> Maybe.map pendingView
|> Maybe.withDefault (Html.div [] [])
]
}
type alias NewItem =
{ name : String, description : String, price : Int, imageUrl : String }
toResult : ( Maybe parsed, FormParser.FieldErrors error ) -> Result (FormParser.FieldErrors error) parsed
toResult ( maybeParsed, fieldErrors ) =
let
isEmptyDict : Bool
isEmptyDict =
if Dict.isEmpty fieldErrors then
True
else
fieldErrors
|> Dict.Extra.any (\_ errors -> List.isEmpty errors)
in
case ( maybeParsed, isEmptyDict ) of
( Just parsed, True ) ->
Ok parsed
_ ->
Err fieldErrors
parseIgnoreErrors : ( Maybe parsed, FormParser.FieldErrors error ) -> Result (FormParser.FieldErrors error) parsed
parseIgnoreErrors ( maybeParsed, fieldErrors ) =
case maybeParsed of
Just parsed ->
Ok parsed
_ ->
Err fieldErrors
pendingView : NewItem -> Html (Pages.Msg.Msg Msg)
pendingView item =
Html.div [ Attr.class "item" ]
[ Html.h2 [] [ Html.text "Preview" ]
, Html.div []
[ Html.h3 [] [ Html.text item.name ]
, Html.p [] [ Html.text item.description ]
, Html.p [] [ "$" ++ String.fromInt item.price |> Html.text ]
]
, Html.div []
[ Html.img
[ Attr.src (item.imageUrl ++ "?ixlib=rb-1.2.1&raw_url=true&q=80&fm=jpg&crop=entropy&cs=tinysrgb&auto=format&fit=crop&w=600&h=903") ]
[]
]
]

View File

@ -1,9 +1,12 @@
module Data.Smoothies exposing (Smoothie, selection)
module Data.Smoothies exposing (Smoothie, create, selection)
import Api.InputObject
import Api.Mutation
import Api.Object.Products
import Api.Query
import Api.Scalar exposing (Uuid(..))
import Graphql.Operation exposing (RootQuery)
import Graphql.Operation exposing (RootMutation, RootQuery)
import Graphql.OptionalArgument exposing (OptionalArgument(..))
import Graphql.SelectionSet as SelectionSet exposing (SelectionSet)
@ -26,3 +29,23 @@ selection =
Api.Object.Products.price
Api.Object.Products.unsplash_image_id
)
create :
{ name : String, description : String, price : Int, imageUrl : String }
-> SelectionSet () RootMutation
create item =
Api.Mutation.insert_products_one identity
{ object =
Api.InputObject.buildProducts_insert_input
(\opts ->
{ opts
| name = Present item.name
, description = Present item.description
, price = Present item.price
, unsplash_image_id = Present item.imageUrl
}
)
}
SelectionSet.empty
|> SelectionSet.nonNullOrFail

View File

@ -152,6 +152,7 @@ type CompleteParser error parsed
= CompleteParser
input : List (Html.Attribute msg) -> RawField -> Html msg
input attrs rawField =
Html.input
(attrs