Replace New route with Admin/Slug.

This commit is contained in:
Dillon Kearns 2023-02-04 17:21:03 -08:00
parent b0d5a3c2e1
commit d140be0f73
5 changed files with 59 additions and 355 deletions

View File

@ -1,4 +1,4 @@
module Route.Posts.Slug_.Edit exposing (ActionData, Data, route, RouteParams, Msg, Model)
module Route.Admin.Slug_ exposing (ActionData, Data, route, RouteParams, Msg, Model)
{-|
@ -9,7 +9,6 @@ module Route.Posts.Slug_.Edit exposing (ActionData, Data, route, RouteParams, Ms
import BackendTask
import BackendTask.Custom
import Date exposing (Date)
import Debug
import Effect
import ErrorPage
import FatalError
@ -116,21 +115,33 @@ data :
-> Server.Request.Parser (BackendTask.BackendTask FatalError.FatalError (Server.Response.Response Data ErrorPage.ErrorPage))
data routeParams =
Server.Request.succeed
(BackendTask.Custom.run "getPost"
(Encode.string routeParams.slug)
(Decode.nullable Post.decoder)
|> BackendTask.allowFatal
|> BackendTask.map
(\maybePost ->
case maybePost of
Just post ->
Server.Response.render
{ post = post
}
(if routeParams.slug == "new" then
Server.Response.render
{ post =
{ slug = ""
, title = ""
, body = ""
, publish = Nothing
}
}
|> BackendTask.succeed
Nothing ->
Route.redirectTo Route.Index
)
else
BackendTask.Custom.run "getPost"
(Encode.string routeParams.slug)
(Decode.nullable Post.decoder)
|> BackendTask.allowFatal
|> BackendTask.map
(\maybePost ->
case maybePost of
Just post ->
Server.Response.render
{ post = post
}
Nothing ->
Server.Response.errorPage ErrorPage.NotFound
)
)
@ -167,7 +178,18 @@ action routeParams =
(\( formResponse, parsedForm ) ->
case parsedForm of
Ok okForm ->
BackendTask.Custom.run "updatePost"
let
createPost : Bool
createPost =
okForm.slug == "new"
in
BackendTask.Custom.run
(if createPost then
"createPost"
else
"updatePost"
)
(Encode.object
[ ( "slug", Encode.string okForm.slug )
, ( "title", Encode.string okForm.title )
@ -183,18 +205,14 @@ action routeParams =
|> BackendTask.allowFatal
|> BackendTask.map
(\() ->
Server.Response.render
{ errors = formResponse }
Route.redirectTo
(Route.Admin__Slug_ { slug = okForm.slug })
)
Err invalidForm ->
BackendTask.map
(\parsed ->
Server.Response.render
{ errors = formResponse }
)
(Pages.Script.log
(Debug.toString parsedForm)
BackendTask.succeed
(Server.Response.render
{ errors = formResponse }
)
)
(Server.Request.formData (Form.initCombined Basics.identity form))
@ -227,7 +245,10 @@ form =
, fieldView "body" body
, fieldView "publish" publish
, if formState.isTransitioning then
Html.button [ Html.Attributes.disabled True ]
Html.button
[ Html.Attributes.disabled True
, Html.Attributes.attribute "aria-busy" "true"
]
[ Html.text "Submitting..." ]
else
@ -247,7 +268,7 @@ form =
)
|> Form.field "body"
(Form.Field.required "Required" Form.Field.text
|> Form.Field.textarea { rows = Just 30, cols = Just 80 }
|> Form.Field.textarea { rows = Just 10, cols = Just 80 }
|> Form.Field.withInitialValue (.body >> Form.Value.string)
)
|> Form.field "publish"

View File

@ -87,7 +87,8 @@ view :
view maybeUrl sharedModel app =
{ title = "Index page"
, body =
[ app.data.posts
[ Html.h1 [] [ Html.text "Posts" ]
, app.data.posts
|> List.map postView
|> Html.ul []
]
@ -97,7 +98,7 @@ view maybeUrl sharedModel app =
postView : Post -> Html.Html msg
postView post =
Html.li []
[ Route.Posts__Slug___Edit { slug = post.slug }
[ Route.Admin__Slug_ { slug = post.slug }
|> Route.link []
[ Html.text post.title
]

View File

@ -1,324 +0,0 @@
module Route.Posts.New exposing (ActionData, Data, route, RouteParams, Msg, Model)
{-|
@docs ActionData, Data, route, RouteParams, Msg, Model
-}
import BackendTask exposing (BackendTask)
import BackendTask.Custom
import Date exposing (Date)
import Dict
import Effect
import ErrorPage
import FatalError
import Form
import Form.Field
import Form.FieldView
import Form.Validation
import Head
import Html exposing (Html)
import Html.Attributes as Attr
import Json.Decode as Decode
import Json.Encode as Encode
import Markdown.Parser
import Markdown.Renderer
import Pages.Msg
import Pages.PageUrl
import Path
import Platform.Sub
import Route
import RouteBuilder
import Server.Request
import Server.Response
import Shared
import View
type alias Model =
{}
type Msg
= NoOp
type alias RouteParams =
{}
route : RouteBuilder.StatefulRoute RouteParams Data ActionData Model Msg
route =
RouteBuilder.buildWithLocalState
{ view = view
, init = init
, update = update
, subscriptions = subscriptions
}
(RouteBuilder.serverRender { data = data, action = action, head = head })
init :
Maybe Pages.PageUrl.PageUrl
-> Shared.Model
-> RouteBuilder.StaticPayload Data ActionData RouteParams
-> ( Model, Effect.Effect Msg )
init pageUrl sharedModel app =
( {}, Effect.none )
update :
Pages.PageUrl.PageUrl
-> Shared.Model
-> RouteBuilder.StaticPayload Data ActionData RouteParams
-> Msg
-> Model
-> ( Model, Effect.Effect msg )
update pageUrl sharedModel app msg model =
case msg of
NoOp ->
( model, Effect.none )
subscriptions :
Maybe Pages.PageUrl.PageUrl
-> RouteParams
-> Path.Path
-> Shared.Model
-> Model
-> Sub Msg
subscriptions maybePageUrl routeParams path sharedModel model =
Platform.Sub.none
type alias Data =
{}
type alias ActionData =
{ errors : Form.Response String
, errorMessage : Maybe String
}
data :
RouteParams
-> Server.Request.Parser (BackendTask.BackendTask FatalError.FatalError (Server.Response.Response Data ErrorPage.ErrorPage))
data routeParams =
Server.Request.succeed (BackendTask.succeed (Server.Response.render {}))
head : RouteBuilder.StaticPayload Data ActionData RouteParams -> List Head.Tag
head app =
[]
view :
Maybe Pages.PageUrl.PageUrl
-> Shared.Model
-> Model
-> RouteBuilder.StaticPayload Data ActionData RouteParams
-> View.View (Pages.Msg.Msg Msg)
view maybeUrl sharedModel model app =
{ title = "Posts.New"
, body =
[ Html.div
[ Attr.style "display" "flex"
, Attr.style "flex" "row"
, Attr.class "hmmm"
]
[ Html.div []
[ Html.h2
[]
[ Html.text "Form" ]
, app.action
|> Maybe.andThen .errorMessage
|> Maybe.map
(\errorMessage ->
Html.p [ Attr.style "color" "red" ] [ Html.text errorMessage ]
)
|> Maybe.withDefault (Html.text "")
, Form.renderHtml
[]
(\renderStyledHtmlUnpack -> Just renderStyledHtmlUnpack.errors)
app
()
(Form.toDynamicTransition "form" form)
]
, postPreview app
]
]
}
postPreview app =
Html.div []
[ Html.h1 []
[ app.pageFormState
|> Dict.get "form"
|> Maybe.andThen (.fields >> Dict.get "title")
|> Maybe.map .value
|> Maybe.withDefault ""
|> Html.text
]
, app.pageFormState
|> Dict.get "form"
|> Maybe.andThen (.fields >> Dict.get "body")
|> Maybe.map .value
|> Maybe.withDefault ""
|> renderMarkdown
]
renderMarkdown : String -> Html msg
renderMarkdown markdown =
markdown
|> Markdown.Parser.parse
|> Result.withDefault []
|> Markdown.Renderer.render Markdown.Renderer.defaultHtmlRenderer
|> Result.withDefault []
|> Html.div []
action :
RouteParams
-> Server.Request.Parser (BackendTask.BackendTask FatalError.FatalError (Server.Response.Response ActionData ErrorPage.ErrorPage))
action routeParams =
Server.Request.map
(\formData ->
case formData of
( formResponse, parsedForm ) ->
case parsedForm of
Ok okForm ->
BackendTask.Custom.run "createPost"
(Encode.object
[ ( "slug", Encode.string okForm.slug )
, ( "title", Encode.string okForm.title )
, ( "body", Encode.string okForm.body )
, ( "publish"
, okForm.publish
|> Maybe.map (Date.toIsoString >> Encode.string)
|> Maybe.withDefault Encode.null
)
]
)
(Decode.oneOf
[ Decode.field "errorMessage" (Decode.string |> Decode.map Err)
, Decode.succeed (Ok ())
]
)
|> BackendTask.allowFatal
|> BackendTask.map
(\result ->
case result of
Ok () ->
Route.redirectTo (Route.Posts__Slug___Edit { slug = okForm.slug })
Err errorMessage ->
Server.Response.render { errors = formResponse, errorMessage = Just errorMessage }
)
Err errors ->
Server.Response.render { errors = formResponse, errorMessage = Nothing }
|> BackendTask.succeed
)
(Server.Request.formData (Form.initCombined Basics.identity form))
form : Form.DoneForm String ParsedForm () (List (Html.Html (Pages.Msg.Msg Msg)))
form =
Form.field
"publish"
(Form.Field.date { invalid = \_ -> "" })
((\title slug body publish ->
{ combine =
ParsedForm
|> Form.Validation.succeed
|> Form.Validation.andMap title
|> Form.Validation.andMap
(slug
|> Form.Validation.andThen
(\slugValue ->
if slugValue |> String.contains " " then
Form.Validation.withError slug
"Cannot contain spaces"
(Form.Validation.mapWithNever identity slug)
else
Form.Validation.mapWithNever identity slug
)
)
|> Form.Validation.andMap body
|> Form.Validation.andMap publish
, view =
\formState ->
let
fieldView label field =
Html.div
[]
[ Html.label
[]
[ Html.text (label ++ " ")
, Form.FieldView.input [] field
, errorsView
formState.errors
field
]
]
in
[ fieldView "title" title
, fieldView "slug" slug
, fieldView "body" body
, fieldView "publish" publish
, if formState.isTransitioning then
Html.button [ Attr.disabled True ]
[ Html.text "Submitting..."
]
else
Html.button []
[ Html.text "Submit"
]
]
}
)
|> Form.init
|> Form.field "title" (Form.Field.required "Required" Form.Field.text)
|> Form.field "slug" (Form.Field.required "Required" Form.Field.text)
|> Form.field "body"
(Form.Field.required "Required" Form.Field.text
|> Form.Field.textarea
{ rows = Just 30, cols = Just 80 }
)
)
type alias ParsedForm =
{ title : String
, slug : String
, body : String
, publish : Maybe Date
}
errorsView :
Form.Errors String
-> Form.Validation.Field String parsed kind
-> Html.Html (Pages.Msg.Msg Msg)
errorsView errors field =
if List.isEmpty (Form.errorsForField field errors) then
Html.div [] []
else
Html.div []
[ Html.ul []
(List.map
(\error ->
Html.li
[ Attr.style "color" "red" ]
[ Html.text error ]
)
(Form.errorsForField field errors)
)
]

View File

@ -4,6 +4,7 @@ import BackendTask exposing (BackendTask)
import Effect exposing (Effect)
import FatalError exposing (FatalError)
import Html exposing (Html)
import Html.Attributes as Attr
import Pages.Flags
import Pages.PageUrl exposing (PageUrl)
import Path exposing (Path)
@ -92,7 +93,10 @@ view :
-> { body : List (Html msg), title : String }
view stars page model toMsg pageView =
{ body =
[ Html.div [] pageView.body
[ Html.div
[ Attr.class "container"
]
pageView.body
]
, title = pageView.title
}

View File

@ -1,3 +1,5 @@
@import "https://unpkg.com/@picocss/pico@1.5.7/css/pico.min.css";
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial,
sans-serif, "Apple Color Emoji", "Segoe UI Emoji";