packages feed

hyperbole-0.3.5: example/Example/Forms.hs

{-# LANGUAGE AllowAmbiguousTypes #-}

module Example.Forms where

import Data.Text as T (Text, elem, length, pack)
import Effectful
import Example.Style qualified as Style
import Web.Hyperbole


page :: (Hyperbole :> es) => Page es Response
page = do
  handle formAction

  load $ do
    pure $ row (pad 20) $ do
      hyper FormView (formView mempty)


data FormView = FormView
  deriving (Generic, Param)


data FormAction = Submit
  deriving (Generic, Param)


instance HyperView FormView where
  type Action FormView = FormAction


-- Form Fields
data User = User Text deriving (Generic, FormField)
data Age = Age Int deriving (Generic, FormField)
data Pass1 = Pass1 Text deriving (Generic, FormField)
data Pass2 = Pass2 Text deriving (Generic, FormField)


formAction :: (Hyperbole :> es) => FormView -> FormAction -> Eff es (View FormView ())
formAction _ Submit = do
  u <- formField @User
  a <- formField @Age
  p1 <- formField @Pass1
  p2 <- formField @Pass2

  case validateUser u a p1 p2 of
    Validation [] -> pure $ userView u a p1
    errs -> pure $ formView errs


validateUser :: User -> Age -> Pass1 -> Pass2 -> Validation
validateUser (User u) (Age a) (Pass1 p1) (Pass2 p2) =
  validation
    [ validate @Age (a < 20) "User must be at least 20 years old"
    , validate @User (T.elem ' ' u) "Username must not contain spaces"
    , validate @User (T.length u < 4) "Username must be at least 4 chars"
    , validate @Pass1 (p1 /= p2) "Passwords did not match"
    , validate @Pass1 (T.length p1 < 8) "Password must be at least 8 chars"
    ]


formView :: Validation -> View FormView ()
formView v = do
  form Submit v (gap 10 . pad 10) $ do
    el Style.h1 "Sign Up"

    field @User id Style.invalid $ do
      label "Username"
      input Username (inp . placeholder "username")
      el_ invalidText

    field @Age id Style.invalid $ do
      label "Age"
      input Number (inp . placeholder "age" . value "0")
      el_ invalidText

    field @Pass1 id Style.invalid $ do
      label "Password"
      input NewPassword (inp . placeholder "password")
      el_ invalidText

    field @Pass2 id id $ do
      label "Repeat Password"
      input NewPassword (inp . placeholder "repeat password")

    submit Style.btn "Submit"
 where
  inp = border 1 . pad 8




userView :: User -> Age -> Pass1 -> View FormView ()
userView (User user) (Age age) (Pass1 pass1) = do
  el (bold . Style.success) "Accepted Signup"
  row (gap 5) $ do
    el_ "Username:"
    el_ $ text user

  row (gap 5) $ do
    el_ "Age:"
    el_ $ text $ pack (show age)

  row (gap 5) $ do
    el_ "Password:"
    el_ $ text pass1