hyperbole-0.1.2: example/Example/Contacts.hs
module Example.Contacts where
import Control.Monad (forM_)
import Data.String.Conversions
import Data.Text (Text)
import Effectful
import Effectful.Dispatch.Dynamic
import Example.Colors
import Example.Effects.Debug
import Example.Effects.Users (User (..), Users)
import Example.Effects.Users qualified as Users
import GHC.Generics (Generic)
import Web.Hyperbole
page :: forall es. (Hyperbole :> es, Users :> es, Debug :> es) => Page es ()
page = do
hyper contacts
hyper contact
load $ do
us <- usersAll
pure $ do
col (pad 10 . gap 10) $ do
viewId Contacts $ allContactsView Nothing us
-- Contacts ----------------------------------------------
data Contacts = Contacts
deriving (Show, Read, Param)
data ContactsAction
= Reload (Maybe Filter)
| Delete Int
deriving (Show, Read, Param)
instance HyperView Contacts where
type Action Contacts = ContactsAction
data Filter
= Active
| Inactive
deriving (Show, Read, Eq)
contacts :: (Hyperbole :> es, Users :> es, Debug :> es) => Contacts -> ContactsAction -> Eff es (View Contacts ())
contacts _ (Reload mf) = do
us <- usersAll
pure $ allContactsView mf us
contacts _ (Delete uid) = do
userDelete uid
us <- usersAll
pure $ allContactsView Nothing us
allContactsView :: Maybe Filter -> [User] -> View Contacts ()
allContactsView fil us = do
row (gap 10) $ do
button (Reload Nothing) (bg GrayLight) "Reload"
dropdown Reload (== fil) id $ do
option Nothing ""
option (Just Active) "Active!"
option (Just Inactive) "Inactive"
target (Contact 2) $ button Edit (bg GrayLight) "Edit 2"
row (pad 10 . gap 10) $ do
let filtered = filter (filterUsers fil) us
forM_ filtered $ \u -> do
el (border 1) $ do
viewId (Contact u.id) $ contactView u
where
filterUsers Nothing _ = True
filterUsers (Just Active) u = u.isActive
filterUsers (Just Inactive) u = not u.isActive
-- Contact ----------------------------------------------------
data Contact = Contact Int
deriving (Show, Read, Param)
data ContactAction
= Edit
| Save
| View
deriving (Show, Read, Param)
instance HyperView Contact where
type Action Contact = ContactAction
data UserForm a = UserForm
{ firstName :: Field a Text
, lastName :: Field a Text
, age :: Field a Int
}
deriving (Generic, Form)
contact :: (Hyperbole :> es, Users :> es, Debug :> es) => Contact -> ContactAction -> Eff es (View Contact ())
contact (Contact uid) a = do
u <- userFind uid
action u a
where
action u View = do
pure $ contactView u
action u Edit = do
pure $ contactEdit u
action u Save = do
delay 1000
UserForm{firstName, lastName, age} <- parseForm
let u' = User{id = u.id, isActive = True, firstName, lastName, age}
userSave u'
pure $ contactView u'
contactView :: User -> View Contact ()
contactView u = do
col (pad 10 . gap 10) $ do
row fld $ do
el id (text "First Name:")
text u.firstName
row fld $ do
el id (text "Last Name:")
text u.lastName
row fld $ do
el id (text "Age:")
text (cs $ show u.age)
row fld $ do
el id (text "Active:")
text (cs $ show u.isActive)
button Edit (bg Primary . color White . hover (bg PrimaryLight . color Dark)) "Edit"
where
fld = gap 10
contactEdit :: User -> View Contact ()
contactEdit u =
onRequest loading $ do
form @UserForm Save (pad 10 . gap 10) $ \f -> do
field fld $ do
label "First Name:"
input Name (value u.firstName) f.firstName
field fld $ do
label "Last Name:"
input Name (value u.lastName) f.lastName
field fld $ do
label "Age:"
input Number (value $ cs $ show u.age) f.age
submit id "Submit"
button View id (text "Cancel")
target Contacts $ button (Delete u.id) (bg Secondary) (text "Delete")
where
loading = el (bg Secondary) "Loading..."
fld = flexRow . gap 10
userFind :: (Hyperbole :> es, Users :> es) => Int -> Eff es User
userFind uid = do
mu <- send (Users.LoadUser uid)
maybe notFound pure mu
usersAll :: (Users :> es) => Eff es [User]
usersAll = send Users.LoadUsers
userSave :: (Users :> es) => User -> Eff es ()
userSave = send . Users.SaveUser
userDelete :: (Users :> es) => Int -> Eff es ()
userDelete = send . Users.DeleteUser