elm-pages-v3-beta/examples/pokedex/src/Page/TailwindForm.elm

1175 lines
34 KiB
Elm
Raw Normal View History

module Page.TailwindForm exposing (Data, Model, Msg, page)
import Browser.Dom
import Css exposing (Color)
import Css.Global
import DataSource exposing (DataSource)
2022-01-04 08:17:07 +03:00
import Date exposing (Date)
import Dict exposing (Dict)
import Form exposing (Form)
import Head
import Head.Seo as Seo
2022-01-05 03:20:44 +03:00
import Html as CoreHtml
import Html.Events
import Html.Styled as Html exposing (Html)
import Html.Styled.Attributes as Attr exposing (css)
import Http
import Icon
import Page exposing (Page, PageWithState, StaticPayload)
import PageServerResponse exposing (PageServerResponse)
import Pages.PageUrl exposing (PageUrl)
import Pages.Url
import Server.Request as Request exposing (Request)
import Shared
import Tailwind.Breakpoints as Bp
import Tailwind.Utilities as Tw
import Task
2022-01-04 08:17:07 +03:00
import Time
import View exposing (View)
type alias Model =
2022-01-06 02:12:58 +03:00
{ form : Form.Model
, flashMessage : Maybe (Result String String)
2022-01-06 02:12:58 +03:00
}
2022-01-06 02:12:58 +03:00
type Msg
= FormMsg Form.Msg
| GotFormResponse (Result Http.Error Form.ServerUpdate)
| MovedToTop
type alias RouteParams =
{}
type alias User =
{ first : String
, last : String
, username : String
, email : String
2022-01-04 08:17:07 +03:00
, birthDay : Date
, checkIn : Date
, checkOut : Date
2022-01-05 22:40:35 +03:00
, rating : Int
, password : ( String, String )
, notificationPreferences : NotificationPreferences
}
type alias NotificationPreferences =
{ comments : Bool
, candidates : Bool
, offers : Bool
, pushNotificationsSetting : PushNotificationsSetting
}
defaultUser : User
defaultUser =
{ first = "jane"
, last = "Doe"
, username = "janedoe"
, email = "janedoe@example.com"
2022-01-04 08:17:07 +03:00
, birthDay = Date.fromCalendarDate 1969 Time.Jul 20
, checkIn = Date.fromCalendarDate 2022 Time.Jan 11
, checkOut = Date.fromCalendarDate 2022 Time.Jan 12
2022-01-05 22:40:35 +03:00
, rating = 5
, password = ( "", "" )
, notificationPreferences =
{ comments = False
, candidates = False
, offers = False
, pushNotificationsSetting = PushNone
}
}
styleAttrs attrs =
List.map Attr.fromUnstyled attrs
usernameInput ({ toInput, toLabel, errors, submitStatus } as info) =
Html.div []
[ Html.div
[ css
[ Bp.sm
[ Tw.grid
, Tw.grid_cols_3
, Tw.gap_4
, Tw.items_start
, Tw.border_t
, Tw.border_gray_200
, Tw.pt_5
]
]
]
[ Html.label
(styleAttrs toInput
++ [ Attr.for "username"
, css
[ Tw.block
, Tw.text_sm
, Tw.font_medium
, Tw.text_gray_700
, Bp.sm
[ Tw.mt_px
, Tw.pt_2
]
]
]
)
[ Html.text "Username" ]
, Html.div
[ css
[ Tw.mt_1
, Bp.sm
[ Tw.mt_0
, Tw.col_span_2
]
]
]
[ Html.div
[ css
[ Tw.max_w_lg
, Tw.flex
, Tw.rounded_md
, Tw.shadow_sm
, Tw.relative
]
]
[ Html.span
[ css
[ Tw.inline_flex
, Tw.items_center
, Tw.px_3
, Tw.rounded_l_md
, Tw.border
, Tw.border_r_0
, Tw.border_gray_300
, Tw.bg_gray_50
, Tw.text_gray_500
, Bp.sm
[ Tw.text_sm
]
]
]
[ Html.text "workcation.com/" ]
, Html.input
(styleAttrs toInput
++ [ Attr.type_ "text"
, Attr.name "username"
, Attr.id "username"
, Attr.attribute "autocomplete" "username"
, css
[ Tw.flex_1
, Tw.block
, Tw.w_full
, Tw.min_w_0
, Tw.rounded_none
, Tw.rounded_r_md
, Tw.border_gray_300
, Css.focus
[ Tw.ring_indigo_500
, Tw.border_indigo_500
]
, Bp.sm
[ Tw.text_sm
]
]
]
)
[]
, Html.div
[ css
[ Tw.absolute
, Tw.inset_y_0
, Tw.right_0
, Tw.pr_3
, Tw.flex
, Tw.items_center
, Tw.pointer_events_none
]
]
[ if errors |> List.isEmpty then
Html.text ""
else
Icon.error
]
]
]
]
, errorsView info
]
validateCapitalized : String -> Result String String
validateCapitalized string =
if string |> String.toList |> List.head |> Maybe.withDefault 'a' |> Char.isUpper then
Ok string
else
Err "Needs to be capitalized"
form : User -> Form String User (Html Form.Msg)
form user =
Form.succeed User
2022-01-05 22:21:43 +03:00
|> Form.with
2022-01-05 22:23:39 +03:00
(Form.text
"first"
2022-01-03 22:14:39 +03:00
(textInput "First name")
|> Form.withInitialValue user.first
|> Form.withClientValidation validateCapitalized
|> Form.required "Required"
)
2022-01-05 22:21:43 +03:00
|> Form.with
2022-01-05 22:23:39 +03:00
(Form.text
"last"
2022-01-03 22:14:39 +03:00
(textInput "Last name")
|> Form.withInitialValue user.last
|> Form.withClientValidation validateCapitalized
|> Form.required "Required"
)
2022-01-05 22:21:43 +03:00
|> Form.with
2022-01-05 22:23:39 +03:00
(Form.text "username" usernameInput
|> Form.withInitialValue user.username
|> Form.withServerValidation
(\username ->
if username == "asdf" then
DataSource.succeed [ "username is taken" ]
else
DataSource.succeed []
)
)
2022-01-05 22:21:43 +03:00
|> Form.with
2022-01-05 22:23:39 +03:00
(Form.text
"email"
2022-01-03 22:14:39 +03:00
(textInput "Email address")
|> Form.withInitialValue user.email
|> Form.email
|> Form.required "Required"
)
2022-01-05 22:21:43 +03:00
|> Form.with
2022-01-10 23:25:35 +03:00
(Form.requiredDate
"dob"
{ missing = "Required"
, invalid = \_ -> "Invalid date"
}
2022-01-03 22:14:39 +03:00
(textInput "Date of Birth")
2022-01-04 08:17:07 +03:00
|> Form.withInitialValue (user.birthDay |> Date.toIsoString)
|> Form.withMinDate (Date.fromCalendarDate 1900 Time.Jan 1)
|> Form.withMaxDate (Date.fromCalendarDate 2022 Time.Jan 1)
2022-01-04 08:17:07 +03:00
|> Form.withServerValidation
(\birthDate ->
let
_ =
2022-01-05 03:20:44 +03:00
birthDate |> Date.toIsoString
2022-01-04 08:17:07 +03:00
in
2022-01-05 03:20:44 +03:00
if birthDate == Date.fromCalendarDate 1969 Time.Jul 20 then
2022-01-04 08:17:07 +03:00
DataSource.succeed [ "No way, that's when the moon landing happened!" ]
else
DataSource.succeed []
)
)
|> Form.with
(Form.requiredDate
"checkin"
{ missing = "Required"
, invalid = \_ -> "Invalid date"
}
(textInput "Check-in")
|> Form.withInitialValue (user.checkIn |> Date.toIsoString)
|> Form.withMinDate (Date.fromCalendarDate 1900 Time.Jan 1)
|> Form.withMaxDate (Date.fromCalendarDate 2022 Time.Jan 1)
)
|> Form.with
(Form.requiredDate
"checkout"
{ missing = "Required"
, invalid = \_ -> "Invalid date"
}
(textInput "Check-out")
|> Form.withInitialValue (user.checkOut |> Date.toIsoString)
|> Form.withMinDate (Date.fromCalendarDate 1900 Time.Jan 1)
|> Form.withMaxDate (Date.fromCalendarDate 2022 Time.Jan 1)
)
2022-01-05 22:40:35 +03:00
|> Form.with
(Form.range
2022-01-05 22:40:35 +03:00
"rating"
{ missing = "Required"
, invalid = \_ -> "Invalid number"
}
{ initial = 3
, min = 1
, max = 5
}
2022-01-05 22:40:35 +03:00
(textInput "Rating")
)
2022-01-04 08:17:07 +03:00
|> Form.wrap wrapSection
|> Form.appendForm (|>)
(Form.succeed Tuple.pair
|> Form.with
(Form.text "password"
(textInput "Password")
|> Form.password
|> Form.required "Required"
)
|> Form.with
(Form.text
"password-confirmation"
(textInput "Password Confirmation")
|> Form.password
|> Form.required "Required"
)
|> Form.validate
(\( password, passwordConfirmation ) ->
if password == passwordConfirmation then
[]
else
[ ( "password-confirmation"
, [ "Must match password" ]
)
]
)
)
|> Form.appendForm (|>)
2022-01-05 03:20:44 +03:00
((Form.succeed NotificationPreferences
2022-01-05 22:21:43 +03:00
|> Form.with
(Form.checkbox
"comments"
--user.checkbox
False
(checkboxInput { name = "Comments", description = "Get notified when someones posts a comment on a posting." })
)
2022-01-05 22:21:43 +03:00
|> Form.with
(Form.checkbox
"candidates"
--user.checkbox
False
(checkboxInput { name = "Candidates", description = "Get notified when a candidate applies for a job." })
)
2022-01-05 22:21:43 +03:00
|> Form.with
(Form.checkbox
"offers"
--user.checkbox
False
(checkboxInput { name = "Offers", description = "Get notified when a candidate accepts or rejects an offer." })
)
2022-01-05 03:20:44 +03:00
|> Form.wrap wrapEmailSection
2022-01-05 04:55:37 +03:00
|> Form.appendForm (|>)
(Form.succeed identity
2022-01-05 22:21:43 +03:00
|> Form.with
(Form.requiredRadio
2022-01-05 05:14:29 +03:00
"push-notifications"
{ missing = "Missing"
, invalid = \_ -> "Invalid option"
}
2022-01-05 05:14:29 +03:00
( ( "PushAll", PushAll )
, [ ( "PushEmail", PushEmail )
, ( "PushNone", PushNone )
]
)
2022-01-05 04:55:37 +03:00
radioInput
wrapPushNotificationsSection
)
2022-01-05 03:20:44 +03:00
)
)
|> Form.wrap wrapNotificationsSections
)
2022-01-11 06:01:47 +03:00
|> Form.appendForm (\() rest -> rest)
(Form.succeed (\_ -> ())
|> Form.with
(Form.checkbox
"acceptTerms"
False
(checkboxInput { name = "Accept terms", description = "Please read the terms before proceeding." })
|> Form.required "Please agree to terms to proceed."
2022-01-11 06:01:47 +03:00
)
)
|> Form.append
(Form.submit
(\{ attrs, formHasErrors } ->
2022-01-03 22:14:39 +03:00
Html.div
[ css
[ Tw.pt_5
]
]
[ Html.div
[ css
[ Tw.flex
, Tw.justify_end
]
]
2022-01-04 01:26:04 +03:00
[ cancelButton
, saveButton formHasErrors attrs
2022-01-03 22:14:39 +03:00
]
]
)
)
|> Form.validate
(\user_ ->
[ ( "checkin"
, if Date.compare user_.checkIn user_.checkOut == GT then
[ "Must be before checkout."
]
else
[]
)
]
)
2022-01-05 04:55:37 +03:00
type PushNotificationsSetting
= PushAll
| PushEmail
| PushNone
saveButton formHasErrors formAttrs =
2022-01-04 01:26:04 +03:00
Html.button
(styleAttrs formAttrs
++ [ css
[ Tw.ml_3
, Tw.inline_flex
, Tw.justify_center
, Tw.py_2
, Tw.px_4
, Tw.border
, Tw.border_transparent
, Tw.shadow_sm
, Tw.text_sm
, Tw.font_medium
, Tw.rounded_md
, Tw.text_white
, Tw.bg_indigo_600
, Css.focus
[ Tw.outline_none
, Tw.ring_2
, Tw.ring_offset_2
, Tw.ring_indigo_500
]
, --if formHasErrors then
-- Css.batch
-- [ Tw.text_gray_200
-- , Tw.bg_indigo_500
-- , Tw.cursor_default
-- ]
--
-- else
Css.hover
[ Tw.bg_indigo_700
]
2022-01-04 01:26:04 +03:00
]
]
)
[ Html.text "Save" ]
cancelButton : Html msg
cancelButton =
Html.button
[ Attr.type_ "button"
, css
[ Tw.bg_white
, Tw.py_2
, Tw.px_4
, Tw.border
, Tw.border_gray_300
, Tw.rounded_md
, Tw.shadow_sm
, Tw.text_sm
, Tw.font_medium
, Tw.text_gray_700
, Css.focus
[ Tw.outline_none
, Tw.ring_2
, Tw.ring_offset_2
, Tw.ring_indigo_500
]
, Css.hover
[ Tw.bg_gray_50
]
]
]
[ Html.text "Cancel" ]
2022-01-06 02:12:58 +03:00
page : PageWithState RouteParams Data Model Msg
page =
Page.serverRender
{ head = head
, data = data
}
2022-01-06 02:12:58 +03:00
|> Page.buildWithLocalState
{ view = view
, update = update
, init = init
, subscriptions = \_ _ _ _ _ -> Sub.none
}
update _ _ _ _ msg model =
case msg of
FormMsg formMsg ->
2022-01-10 22:38:25 +03:00
model.form
|> Form.update FormMsg GotFormResponse (form defaultUser) formMsg
|> Tuple.mapFirst (\newFormModel -> { model | form = newFormModel })
GotFormResponse result ->
case result of
Ok updatedFormModel ->
if Form.hasErrors2 model.form then
( model, Cmd.none )
|> withFlash (Err "Failed to submit or had errors")
else
( model, Browser.Dom.setViewport 0 0 |> Task.perform (\() -> MovedToTop) )
|> withFlash (Ok "Success! Submitted form from Elm")
Err _ ->
( model, Cmd.none )
2022-01-06 02:12:58 +03:00
MovedToTop ->
( model, Cmd.none )
2022-01-06 02:12:58 +03:00
withFlash : Result String String -> ( Model, Cmd Msg ) -> ( Model, Cmd Msg )
withFlash flashMessage ( model, cmd ) =
( { model | flashMessage = Just flashMessage }, cmd )
2022-01-06 03:11:15 +03:00
init _ _ static =
( { form = static.data.initialForm
, flashMessage =
static.data.user
|> Maybe.map
(\user_ ->
Ok ("Successfully received user " ++ user_.first ++ " " ++ user_.last)
)
}
, Cmd.none
)
type alias Data =
{ user : Maybe User
, initialForm : Form.Model
}
data : RouteParams -> Request (DataSource (PageServerResponse Data))
data routeParams =
Request.oneOf
2022-01-14 04:17:52 +03:00
[ Form.submitHandlers
(form defaultUser)
(\model decoded ->
DataSource.succeed
{ user = Result.toMaybe decoded
, initialForm = model
2022-01-14 04:17:52 +03:00
}
)
, { user = Nothing
, initialForm = Form.init (form defaultUser)
}
|> PageServerResponse.RenderPage
|> DataSource.succeed
|> Request.succeed
]
head :
StaticPayload Data RouteParams
-> List Head.Tag
head static =
Seo.summary
{ canonicalUrlOverride = Nothing
, siteName = "elm-pages"
, image =
{ url = Pages.Url.external "TODO"
, alt = "elm-pages logo"
, dimensions = Nothing
, mimeType = Nothing
}
, description = "TODO"
, locale = Nothing
, title = "TODO title" -- metadata.title -- TODO
}
|> Seo.website
wrapSection : List (Html msg) -> Html msg
wrapSection children =
2022-01-03 22:55:45 +03:00
Html.div []
[ Html.div []
[ Html.h3
[ css
[ Tw.text_lg
, Tw.leading_6
, Tw.font_medium
, Tw.text_gray_900
]
]
[ Html.text "Profile" ]
, Html.p
[ css
[ Tw.mt_1
, Tw.max_w_2xl
, Tw.text_sm
, Tw.text_gray_500
]
]
[ Html.text "This information will be displayed publicly so be careful what you share." ]
]
, Html.div
[ css
[ Tw.mt_6
, Tw.space_y_6
, Bp.sm
[ Tw.mt_5
, Tw.space_y_5
]
]
]
children
2022-01-03 22:55:45 +03:00
]
2022-01-06 02:12:58 +03:00
formModelView formModel =
formModel
|> Debug.toString
|> Html.text
|> List.singleton
|> Html.pre
[ Attr.style "white-space" "break-spaces"
]
2022-01-06 02:12:58 +03:00
view :
Maybe PageUrl
-> Shared.Model
2022-01-06 02:12:58 +03:00
-> Model
-> StaticPayload Data RouteParams
-> View Msg
2022-01-06 02:12:58 +03:00
view maybeUrl sharedModel model static =
let
user : User
user =
static.data.user
|> Maybe.withDefault defaultUser
in
{ title = "Form Example"
, body =
2022-01-06 03:11:15 +03:00
[ Html.div
[]
[ Css.Global.global Tw.globalStyles
, model.flashMessage
|> Maybe.map flashView
|> Maybe.withDefault (Html.p [] [])
2022-01-10 22:38:25 +03:00
, Html.p []
[ if Form.isSubmitting model.form then
Html.text "Submitting..."
else
Html.text ""
]
2022-01-03 22:14:39 +03:00
, Html.div
[ css
[ Tw.flex
, Tw.flex_col
, Tw.items_center
, Tw.mt_8
, Tw.border_gray_700
, Tw.rounded_lg
]
2022-01-03 22:14:39 +03:00
]
[ form user
|> Form.toHtml { pageReloadSubmit = False }
2022-01-03 22:14:39 +03:00
(\attrs children -> Html.form (List.map Attr.fromUnstyled attrs) children)
2022-01-06 03:11:15 +03:00
model.form
2022-01-06 02:12:58 +03:00
|> Html.map FormMsg
2022-01-03 22:14:39 +03:00
]
]
|> Html.toUnstyled
]
}
2022-01-03 22:14:39 +03:00
successColor : Color
successColor =
Css.rgb 163 251 163
errorColor : Color
errorColor =
Css.rgb 251 163 163
flashView : Result String String -> Html msg
flashView message =
Html.p
[ css
[ Css.backgroundColor
(case message of
Ok _ ->
successColor
Err _ ->
errorColor
)
, Tw.p_4
]
]
[ Html.text <|
case message of
Ok okMessage ->
okMessage
Err error ->
"Something went wrong: " ++ error
]
textInput labelText ({ toInput, toLabel, errors, submitStatus } as info) =
2022-01-03 22:14:39 +03:00
Html.div
[ css
[ Bp.sm
[ Tw.grid
, Tw.grid_cols_3
, Tw.gap_4
, Tw.items_start
, Tw.border_t
, Tw.border_gray_200
, Tw.pt_5
]
]
2022-01-03 22:14:39 +03:00
]
[ Html.label
([ css
[ Tw.block
, Tw.text_sm
, Tw.font_medium
, Tw.text_gray_700
, Bp.sm
[ Tw.mt_px
, Tw.pt_2
]
]
2022-01-03 22:14:39 +03:00
]
++ styleAttrs toLabel
)
[ Html.text labelText ]
, Html.div
[ css
[ Tw.mt_1
, Bp.sm
[ Tw.mt_0
, Tw.col_span_2
]
2022-01-03 22:14:39 +03:00
]
]
[ Html.input
(styleAttrs toInput
++ [ --Attr.attribute "autocomplete" "given-name",
css
[ Tw.max_w_lg
, Tw.block
, Tw.w_full
, Tw.shadow_sm
, Tw.border_gray_300
, Tw.rounded_md
, Css.focus
[ Tw.ring_indigo_500
, Tw.border_indigo_500
]
, Bp.sm
2022-01-03 22:14:39 +03:00
[ Tw.max_w_xs
, Tw.text_sm
]
]
2022-01-03 22:14:39 +03:00
]
)
[]
]
, errorsView info
]
errorsView : { a | errors : List String, submitStatus : Form.SubmitStatus, status : Form.FieldStatus } -> Html msg
errorsView { errors, submitStatus, status } =
Html.p
[ css
[ Tw.mt_2
, Tw.text_sm
, Tw.text_red_600
2022-01-04 08:17:07 +03:00
]
]
[ if (status |> Form.isAtLeast Form.Focused) || submitStatus == Form.Submitting || submitStatus == Form.Submitted then
errors
|> String.join "\n"
|> Html.text
else
Html.text ""
2022-01-03 22:14:39 +03:00
]
textInput2 =
Html.div
[ css
[ Bp.sm
[ Tw.grid
, Tw.grid_cols_3
, Tw.gap_4
, Tw.items_start
, Tw.border_t
, Tw.border_gray_200
, Tw.pt_5
]
2022-01-03 22:14:39 +03:00
]
]
[ Html.label
[ Attr.for "first-name"
, css
[ Tw.block
, Tw.text_sm
, Tw.font_medium
, Tw.text_gray_700
, Bp.sm
[ Tw.mt_px
, Tw.pt_2
]
]
2022-01-03 22:14:39 +03:00
]
[ Html.text "First name" ]
, Html.div
[ css
[ Tw.mt_1
, Bp.sm
[ Tw.mt_0
, Tw.col_span_2
]
2022-01-03 22:14:39 +03:00
]
]
[ Html.input
[ Attr.type_ "text"
, Attr.name "first-name"
, Attr.id "first-name"
, Attr.attribute "autocomplete" "given-name"
, css
[ Tw.max_w_lg
, Tw.block
, Tw.w_full
, Tw.shadow_sm
, Tw.border_gray_300
, Tw.rounded_md
, Css.focus
[ Tw.ring_indigo_500
, Tw.border_indigo_500
]
2022-01-03 22:14:39 +03:00
, Bp.sm
[ Tw.max_w_xs
, Tw.text_sm
]
]
]
2022-01-03 22:14:39 +03:00
[]
]
]
2022-01-11 06:01:47 +03:00
checkboxInput { name, description } ({ toLabel, toInput, errors } as info) =
Html.div
[ css
[ Tw.max_w_lg
, Tw.space_y_4
]
]
[ Html.div
[ css
[ Tw.relative
, Tw.flex
, Tw.items_start
]
]
[ Html.div
[ css
[ Tw.flex
, Tw.items_center
, Tw.h_5
]
]
[ Html.input
(styleAttrs toInput
++ [ css
[ Tw.h_4
, Tw.w_4
, Tw.text_indigo_600
, Tw.border_gray_300
, Tw.rounded
, Css.focus
[ Tw.ring_indigo_500
]
]
]
)
[]
]
, Html.div
[ css
[ Tw.ml_3
, Tw.text_sm
]
]
[ Html.label
(styleAttrs toLabel
++ [ css
[ Tw.font_medium
, Tw.text_gray_700
]
]
)
[ Html.text name ]
, Html.p
[ css
[ Tw.text_gray_500
]
]
[ Html.text description ]
]
]
2022-01-11 06:01:47 +03:00
, errorsView info
]
2022-01-03 22:14:39 +03:00
2022-01-05 03:20:44 +03:00
wrapNotificationsSections children =
Html.div
[ css
[ Tw.divide_y
, Tw.divide_gray_200
, Tw.pt_8
, Tw.space_y_6
, Bp.sm
[ Tw.pt_10
, Tw.space_y_5
]
]
]
[ Html.div []
[ Html.h3
[ css
[ Tw.text_lg
, Tw.leading_6
, Tw.font_medium
, Tw.text_gray_900
]
]
[ Html.text "Notifications" ]
, Html.p
[ css
[ Tw.mt_1
, Tw.max_w_2xl
, Tw.text_sm
, Tw.text_gray_500
]
]
[ Html.text "We'll always let you know about important changes, but you pick what else you want to hear about." ]
]
, Html.div
[ css
[ Tw.space_y_6
, Tw.divide_y
, Tw.divide_gray_200
, Bp.sm
[ Tw.space_y_5
]
]
]
children
]
wrapEmailSection children =
Html.div
[ css
[ Tw.pt_6
, Bp.sm
[ Tw.pt_5
]
]
]
[ Html.div
[ Attr.attribute "role" "group"
, Attr.attribute "aria-labelledby" "label-email"
]
[ Html.div
[ css
[ Bp.sm
[ Tw.grid
, Tw.grid_cols_3
, Tw.gap_4
, Tw.items_baseline
]
]
]
[ Html.div []
[ Html.div
[ css
[ Tw.text_base
, Tw.font_medium
, Tw.text_gray_900
, Bp.sm
[ Tw.text_sm
, Tw.text_gray_700
]
]
, Attr.id "label-email"
]
[ Html.text "By Email" ]
]
, Html.div
[ css
[ Tw.mt_4
, Bp.sm
[ Tw.mt_0
, Tw.col_span_2
]
]
]
[ Html.div
[ css
[ Tw.max_w_lg
, Tw.space_y_4
]
]
children
]
]
]
]
radioInput item { toLabel, toInput, errors, status } =
2022-01-05 04:55:37 +03:00
Html.div
[ css
[ Tw.flex
, Tw.items_center
]
]
[ Html.input
(styleAttrs toInput
2022-01-05 19:08:55 +03:00
++ [ css
2022-01-05 04:55:37 +03:00
[ Tw.h_4
, Tw.w_4
, Tw.text_indigo_600
, Tw.border_gray_300
, Css.focus
[ Tw.ring_indigo_500
]
]
]
)
[]
, Html.label
(styleAttrs toLabel
2022-01-05 19:08:55 +03:00
++ [ css
2022-01-05 04:55:37 +03:00
[ Tw.ml_3
, Tw.block
, Tw.text_sm
, Tw.font_medium
, Tw.text_gray_700
]
]
)
[ (case item of
PushAll ->
"Everything"
PushEmail ->
"Same as email"
PushNone ->
"No push notifications"
)
|> Html.text
]
]
wrapPushNotificationsSection ({ errors, submitStatus } as info) children =
2022-01-05 03:20:44 +03:00
Html.div
[ css
[ Tw.pt_6
, Bp.sm
[ Tw.pt_5
]
]
]
[ Html.div
[ Attr.attribute "role" "group"
, Attr.attribute "aria-labelledby" "label-notifications"
]
[ Html.div
[ css
[ Bp.sm
[ Tw.grid
, Tw.grid_cols_3
, Tw.gap_4
, Tw.items_baseline
]
]
]
[ Html.div []
[ Html.div
[ css
[ Tw.text_base
, Tw.font_medium
, Tw.text_gray_900
, Bp.sm
[ Tw.text_sm
, Tw.text_gray_700
]
]
, Attr.id "label-notifications"
]
[ Html.text "Push Notifications" ]
]
, Html.div
[ css
[ Bp.sm
[ Tw.col_span_2
]
]
]
[ Html.div
[ css
[ Tw.max_w_lg
]
]
[ Html.p
[ css
[ Tw.text_sm
, Tw.text_gray_500
]
]
[ Html.text "These are delivered via SMS to your mobile phone." ]
, Html.div
[ css
[ Tw.mt_4
, Tw.space_y_4
]
]
2022-01-05 04:55:37 +03:00
children
2022-01-05 03:20:44 +03:00
]
]
]
]
, errorsView info
2022-01-05 03:20:44 +03:00
]