feat: Hyperbole port compiles and builds successfully

- All page modules now use proper Page es '[ViewId] type with hyper embedding
- Fixed view DSL: tag calls, String/Text conversions, polymorphic context types
- DB effect uses convenience wrappers (lowercase) with send
- HyperView instances have DB :> es, IOE :> es constraints
- Main.hs uses liveAppWith with proper router
- Static file serving deferred (will use nginx or wai-app-static)
- Warning suppressions added where needed
- Build produces working 25MB sis-server binary
This commit is contained in:
2026-07-16 06:50:20 -04:00
parent c45ae41645
commit a717d619d3
9 changed files with 62 additions and 49 deletions
+7 -5
View File
@@ -1,10 +1,13 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -Wno-unused-imports -Wno-missing-export-lists #-}
module Main where module Main where
import Effectful import Effectful
import Network.Wai.Application.Static qualified as Static
import Network.Wai.Handler.Warp qualified as Warp import Network.Wai.Handler.Warp qualified as Warp
import Network.Wai.Middleware.Static qualified as Static
import System.Directory (createDirectoryIfMissing) import System.Directory (createDirectoryIfMissing)
import System.FilePath (takeDirectory) import System.FilePath (takeDirectory)
@@ -34,17 +37,16 @@ main = do
let port = 8080 let port = 8080
putStrLn $ "[sis] listening on 0.0.0.0:" <> show port putStrLn $ "[sis] listening on 0.0.0.0:" <> show port
Warp.run port $ let app = liveAppWith
Static.staticPolicy (Static.addBase "frontend/static") $
liveAppWith
(ServerOptions (ServerOptions
{ toDocument = document documentHead { toDocument = document documentHead
, serverError = defaultError , serverError = defaultError
, parseRequestBody = defaultParseRequestBodyOptions , parseRequestBody = defaultParseRequestBodyOptions
}) })
(runDB conn $ routeRequest router) (runDB conn $ routeRequest router)
Warp.run port $ app
router :: (Hyperbole :> es, DB :> es) => AppRoute -> Eff es Response router :: (Hyperbole :> es, DB :> es, IOE :> es) => AppRoute -> Eff es Response
router RouteHome = do router RouteHome = do
redirect (routeUri RouteDashboard) redirect (routeUri RouteDashboard)
router RouteLogin = runPage Sis.Page.Login.page router RouteLogin = runPage Sis.Page.Login.page
+2
View File
@@ -70,6 +70,8 @@ executables:
- effectful - effectful
- hyperbole - hyperbole
- sis-server - sis-server
- wai-app-static
- wai-extra
tests: tests:
sis-server-test: sis-server-test:
+2
View File
@@ -101,6 +101,8 @@ executable sis-server
, text , text
, time , time
, wai , wai
, wai-app-static
, wai-extra
, warp , warp
default-language: Haskell2010 default-language: Haskell2010
+10 -8
View File
@@ -1,8 +1,10 @@
{-# OPTIONS_GHC -Wno-unused-imports -Wno-unused-do-bind -Wno-name-shadowing -Wno-redundant-constraints -Wno-redundant-constraints #-}
{-# LANGUAGE FlexibleContexts, DeriveAnyClass, DeriveGeneric, MultiParamTypeClasses, FlexibleInstances, UndecidableInstances, TypeApplications, TypeOperators, OverloadedStrings #-} {-# LANGUAGE FlexibleContexts, DeriveAnyClass, DeriveGeneric, MultiParamTypeClasses, FlexibleInstances, UndecidableInstances, TypeApplications, TypeOperators, OverloadedStrings #-}
module Sis.Page.Activity (page) where module Sis.Page.Activity (page) where
import Data.Text (Text) import Data.Text (Text)
import Data.Text qualified as T
import Effectful import Effectful
import Sis.Database import Sis.Database
@@ -34,10 +36,10 @@ instance (DB :> es, IOE :> es) => HyperView ActivityPage es where
Just us -> do Just us -> do
hhs <- getUserHouseholds (UserId (usUserId us)) hhs <- getUserHouseholds (UserId (usUserId us))
case hhs of case hhs of
[] -> pure (el "No households") [] -> pure $ hyper ActivityPage $ el "No households"
(h : _) -> do (h : _) -> do
log <- getActivityLog (unHouseholdId (householdId h)) pageNum 20 log <- getActivityLog (unHouseholdId (householdId h)) pageNum 20
pure (activityView log) pure $ hyper ActivityPage $ activityView log
activityView :: ActivityLogPage -> View ActivityPage () activityView :: ActivityLogPage -> View ActivityPage ()
activityView log = do activityView log = do
@@ -53,7 +55,7 @@ activityView log = do
then button (GoToPage (alpPage log - 1)) @ att "class" nbButtonClass $ text "Previous" then button (GoToPage (alpPage log - 1)) @ att "class" nbButtonClass $ text "Previous"
else none else none
el @ att "style" "align-self:center" $ el @ att "style" "align-self:center" $
text ("Page " <> show (alpPage log) <> " of " <> show totalPages) text ("Page " <> T.pack (show (alpPage log)) <> " of " <> T.pack (show totalPages))
if alpPage log < totalPages if alpPage log < totalPages
then button (GoToPage (alpPage log + 1)) @ att "class" nbButtonClass $ text "Next" then button (GoToPage (alpPage log + 1)) @ att "class" nbButtonClass $ text "Next"
else none else none
@@ -75,24 +77,24 @@ entryRow e = do
text (" " <> statusText <> " ") text (" " <> statusText <> " ")
el @ att "style" "font-weight:500" $ text (aleChoreName e) el @ att "style" "font-weight:500" $ text (aleChoreName e)
el @ att "style" "opacity:0.5" $ el @ att "style" "opacity:0.5" $
text ("on " <> show (aleOccurrenceDate e)) text ("on " <> T.pack (show (aleOccurrenceDate e)))
case activityNote act of case activityNote act of
Just note -> Just note ->
el @ att "style" "opacity:0.5;font-style:italic;margin-left:0.5rem" $ el @ att "style" "opacity:0.5;font-style:italic;margin-left:0.5rem" $
text ("\"" <> note <> "\"") text ("\"" <> note <> "\"")
Nothing -> none Nothing -> none
page :: (Hyperbole :> es, DB :> es) => Eff es (View ActivityPage ()) page :: (Hyperbole :> es, DB :> es, IOE :> es) => Page es '[ActivityPage]
page = do page = do
mSession <- lookupSession @UserSession mSession <- lookupSession @UserSession
case mSession of case mSession of
Nothing -> do Nothing -> do
redirect (routeUri RouteLogin) redirect (routeUri RouteLogin)
pure (el "Redirecting...") pure $ hyper ActivityPage $ el "Redirecting..."
Just us -> do Just us -> do
hhs <- getUserHouseholds (UserId (usUserId us)) hhs <- getUserHouseholds (UserId (usUserId us))
case hhs of case hhs of
[] -> pure (el "No households") [] -> pure $ hyper ActivityPage $ el "No households"
(h : _) -> do (h : _) -> do
log <- getActivityLog (unHouseholdId (householdId h)) 1 20 log <- getActivityLog (unHouseholdId (householdId h)) 1 20
pure (activityView log) pure $ hyper ActivityPage $ activityView log
+10 -8
View File
@@ -1,8 +1,10 @@
{-# OPTIONS_GHC -Wno-unused-imports -Wno-unused-do-bind -Wno-name-shadowing -Wno-redundant-constraints -Wno-redundant-constraints #-}
{-# LANGUAGE FlexibleContexts, DeriveAnyClass, DeriveGeneric, MultiParamTypeClasses, FlexibleInstances, UndecidableInstances, TypeApplications, TypeOperators, OverloadedStrings #-} {-# LANGUAGE FlexibleContexts, DeriveAnyClass, DeriveGeneric, MultiParamTypeClasses, FlexibleInstances, UndecidableInstances, TypeApplications, TypeOperators, OverloadedStrings #-}
module Sis.Page.Chores (page) where module Sis.Page.Chores (page) where
import Data.Text (Text) import Data.Text (Text)
import Data.Text qualified as T
import Data.Time (getCurrentTime) import Data.Time (getCurrentTime)
import Effectful import Effectful
@@ -34,12 +36,12 @@ instance (DB :> es, IOE :> es) => HyperView ChoresPage es where
Just us -> do Just us -> do
hhs <- getUserHouseholds (UserId (usUserId us)) hhs <- getUserHouseholds (UserId (usUserId us))
case hhs of case hhs of
[] -> pure (el "No households") [] -> pure $ hyper ChoresPage $ el "No households"
(h : _) -> do (h : _) -> do
chores' <- getChores (unHouseholdId (householdId h)) chores' <- getChores (unHouseholdId (householdId h))
pure (choresView chores') pure $ hyper ChoresPage $ choresView chores'
update (CDeleteChore cid) = do update (CDeleteChore cid) = do
Sis.Database.DeleteChore (unChoreId cid) deleteChore (unChoreId cid)
update CRefreshChores update CRefreshChores
update CNewChore = do update CNewChore = do
-- TODO: show chore creation form -- TODO: show chore creation form
@@ -73,23 +75,23 @@ scheduleBadge ScheduleRecurring{} = "recurring"
scheduleLabel :: Schedule -> Text scheduleLabel :: Schedule -> Text
scheduleLabel ScheduleSometime = "Sometime" scheduleLabel ScheduleSometime = "Sometime"
scheduleLabel (ScheduleOneOff d _) = "One-off on " <> show d scheduleLabel (ScheduleOneOff d _) = "One-off on " <> T.pack (show d)
scheduleLabel (ScheduleRecurring p _ mt _ _) = scheduleLabel (ScheduleRecurring p _ mt _ _) =
let pText = case p of PeriodDaily -> "daily"; PeriodWeekly -> "weekly"; PeriodMonthly -> "monthly" let pText = case p of PeriodDaily -> "daily"; PeriodWeekly -> "weekly"; PeriodMonthly -> "monthly"
timePart = maybe "" (\t -> " at " <> t) mt timePart = maybe "" (\t -> " at " <> t) mt
in "Recurs " <> pText <> timePart in "Recurs " <> pText <> timePart
page :: (Hyperbole :> es, DB :> es) => Eff es (View ChoresPage ()) page :: (Hyperbole :> es, DB :> es, IOE :> es) => Page es '[ChoresPage]
page = do page = do
mSession <- lookupSession @UserSession mSession <- lookupSession @UserSession
case mSession of case mSession of
Nothing -> do Nothing -> do
redirect (routeUri RouteLogin) redirect (routeUri RouteLogin)
pure (el "Redirecting...") pure $ hyper ChoresPage $ el "Redirecting..."
Just us -> do Just us -> do
hhs <- getUserHouseholds (UserId (usUserId us)) hhs <- getUserHouseholds (UserId (usUserId us))
case hhs of case hhs of
[] -> pure (el "No households") [] -> pure $ hyper ChoresPage $ el "No households"
(h : _) -> do (h : _) -> do
chores' <- getChores (unHouseholdId (householdId h)) chores' <- getChores (unHouseholdId (householdId h))
pure (choresView chores') pure $ hyper ChoresPage $ choresView chores'
+14 -12
View File
@@ -1,9 +1,11 @@
{-# OPTIONS_GHC -Wno-unused-imports -Wno-unused-do-bind -Wno-name-shadowing #-}
{-# LANGUAGE FlexibleContexts, DeriveAnyClass, DeriveGeneric, MultiParamTypeClasses, FlexibleInstances, UndecidableInstances, TypeApplications, TypeOperators, OverloadedStrings #-} {-# LANGUAGE FlexibleContexts, DeriveAnyClass, DeriveGeneric, MultiParamTypeClasses, FlexibleInstances, UndecidableInstances, TypeApplications, TypeOperators, OverloadedStrings #-}
module Sis.Page.Dashboard (page) where module Sis.Page.Dashboard (page) where
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Text (Text) import Data.Text (Text)
import Data.Text qualified as T
import Data.Time (getCurrentTime, utctDay) import Data.Time (getCurrentTime, utctDay)
import Effectful import Effectful
@@ -35,10 +37,10 @@ instance (DB :> es, IOE :> es) => HyperView DashboardPage es where
today <- liftIO (utctDay <$> getCurrentTime) today <- liftIO (utctDay <$> getCurrentTime)
hhs <- getUserHouseholds (UserId (usUserId us)) hhs <- getUserHouseholds (UserId (usUserId us))
case hhs of case hhs of
[] -> pure (el "No households found") [] -> pure $ hyper DashboardPage $ el "No households"
(h : _) -> do (h : _) -> do
dash <- getDashboard (unHouseholdId (householdId h)) today dash <- getDashboard (unHouseholdId (householdId h)) today
pure (dashboardView dash) pure $ hyper DashboardPage $ dashboardView dash
update (CheckOff _oid) = do update (CheckOff _oid) = do
-- TODO: wire up to activity form -- TODO: wire up to activity form
update RefreshDashboard update RefreshDashboard
@@ -49,9 +51,9 @@ dashboardView dash = do
el @ att "class" nbFontHeading1Class $ text "Dashboard" el @ att "class" nbFontHeading1Class $ text "Dashboard"
el @ att "style" "display:flex;gap:1rem;margin-bottom:1.5rem" $ do el @ att "style" "display:flex;gap:1rem;margin-bottom:1.5rem" $ do
let stats = dashStats dash let stats = dashStats dash
statTile "Overdue" (show (dsOverdue stats)) colorRed statTile "Overdue" (T.pack (show (dsOverdue stats))) colorRed
statTile "Due Today" (show (dsDueToday stats)) colorYellow statTile "Due Today" (T.pack (show (dsDueToday stats))) colorYellow
statTile "Done This Week" (show (dsDoneThisWeek stats)) colorGreen statTile "Done This Week" (T.pack (show (dsDoneThisWeek stats))) colorGreen
el @ att "class" nbFontHeading2Class $ text "Overdue & Due Today" el @ att "class" nbFontHeading2Class $ text "Overdue & Due Today"
if null (dashDueItems dash) if null (dashDueItems dash)
then el @ att "style" "opacity:0.5" $ text "Nothing due! Great job." then el @ att "style" "opacity:0.5" $ text "Nothing due! Great job."
@@ -69,7 +71,7 @@ statTile label count color = do
el @ att "class" nbFontHeading1Class $ text count el @ att "class" nbFontHeading1Class $ text count
text label text label
dueItemRow :: DueItem -> View ctx () dueItemRow :: DueItem -> View DashboardPage ()
dueItemRow di = do dueItemRow di = do
el @ att "class" nbListItemClass @ att "style" "display:flex;justify-content:space-between;align-items:center;padding:0.5rem" $ do el @ att "class" nbListItemClass @ att "style" "display:flex;justify-content:space-between;align-items:center;padding:0.5rem" $ do
el @ att "style" "display:flex;gap:0.5rem;align-items:center" $ do el @ att "style" "display:flex;gap:0.5rem;align-items:center" $ do
@@ -82,7 +84,7 @@ dueItemRow di = do
Nothing -> none Nothing -> none
button (CheckOff (occurrenceId (diOccurrence di))) @ att "class" nbButtonClass @ att "style" "font-size:0.85rem" $ text "Check Off" button (CheckOff (occurrenceId (diOccurrence di))) @ att "class" nbButtonClass @ att "style" "font-size:0.85rem" $ text "Check Off"
completedItemRow :: CompletedItem -> View ctx () completedItemRow :: CompletedItem -> View DashboardPage ()
completedItemRow ci = do completedItemRow ci = do
el @ att "class" nbListItemClass @ att "style" "padding:0.5rem" $ do el @ att "class" nbListItemClass @ att "style" "padding:0.5rem" $ do
let act = ciActivity ci let act = ciActivity ci
@@ -90,23 +92,23 @@ completedItemRow ci = do
ActivityCompleted -> "COMPLETED" ActivityCompleted -> "COMPLETED"
ActivitySkipped -> "SKIPPED" ActivitySkipped -> "SKIPPED"
el @ att "class" nbBadgeClass @ att "style" ("margin-right:0.5rem;background:" <> colorGreen <> ";color:#000") $ text statusText el @ att "class" nbBadgeClass @ att "style" ("margin-right:0.5rem;background:" <> colorGreen <> ";color:#000") $ text statusText
text (ciUserName ci <> " " <> show (activityStatus act) <> " " <> ciChoreName ci) text (ciUserName ci <> " " <> T.pack (show (activityStatus act)) <> " " <> ciChoreName ci)
case activityNote act of case activityNote act of
Just note -> el @ att "style" "opacity:0.5;font-style:italic;margin-left:0.5rem" $ text ("\"" <> note <> "\"") Just note -> el @ att "style" "opacity:0.5;font-style:italic;margin-left:0.5rem" $ text ("\"" <> note <> "\"")
Nothing -> none Nothing -> none
page :: (Hyperbole :> es, DB :> es) => Eff es (View DashboardPage ()) page :: (Hyperbole :> es, DB :> es, IOE :> es) => Page es '[DashboardPage]
page = do page = do
mSession <- lookupSession @UserSession mSession <- lookupSession @UserSession
case mSession of case mSession of
Nothing -> do Nothing -> do
redirect (routeUri RouteLogin) redirect (routeUri RouteLogin)
pure (el "Redirecting...") pure $ hyper DashboardPage $ el "Redirecting..."
Just us -> do Just us -> do
today <- liftIO (utctDay <$> getCurrentTime) today <- liftIO (utctDay <$> getCurrentTime)
hhs <- getUserHouseholds (UserId (usUserId us)) hhs <- getUserHouseholds (UserId (usUserId us))
case hhs of case hhs of
[] -> pure (el "No households — create one first") [] -> pure $ hyper DashboardPage $ el "No households"
(h : _) -> do (h : _) -> do
dash <- getDashboard (unHouseholdId (householdId h)) today dash <- getDashboard (unHouseholdId (householdId h)) today
pure (dashboardView dash) pure $ hyper DashboardPage $ dashboardView dash
+8 -7
View File
@@ -1,3 +1,4 @@
{-# OPTIONS_GHC -Wno-unused-imports -Wno-unused-do-bind -Wno-name-shadowing -Wno-redundant-constraints #-}
{-# LANGUAGE FlexibleContexts, DeriveAnyClass, DeriveGeneric, MultiParamTypeClasses, FlexibleInstances, UndecidableInstances, TypeApplications, TypeOperators, OverloadedStrings #-} {-# LANGUAGE FlexibleContexts, DeriveAnyClass, DeriveGeneric, MultiParamTypeClasses, FlexibleInstances, UndecidableInstances, TypeApplications, TypeOperators, OverloadedStrings #-}
module Sis.Page.Household (page) where module Sis.Page.Household (page) where
@@ -39,7 +40,7 @@ instance (DB :> es, IOE :> es) => HyperView HouseholdPage es where
let hid = unHouseholdId (householdId h) let hid = unHouseholdId (householdId h)
mems <- getMembers hid mems <- getMembers hid
invs <- getInvites hid invs <- getInvites hid
pure (householdView h mems invs) pure $ hyper HouseholdPage $ householdView h mems invs
update CreateInviteAction = do update CreateInviteAction = do
mUser <- lookupSession @UserSession mUser <- lookupSession @UserSession
case mUser of case mUser of
@@ -69,7 +70,7 @@ householdView :: Household -> [Membership] -> [Invite] -> View HouseholdPage ()
householdView h mems invs = do householdView h mems invs = do
el @ att "class" nbContainerClass @ att "style" "max-width:960px;margin:0 auto" $ do el @ att "class" nbContainerClass @ att "style" "max-width:960px;margin:0 auto" $ do
el @ att "class" nbFontHeading1Class $ text (householdName h) el @ att "class" nbFontHeading1Class $ text (householdName h)
el @ att "style" "opacity:0.7" $ text (T.pack (show (length mems) <> " members") el @ att "style" "opacity:0.7" $ text (T.pack (show (length mems)) <> " members")
el @ att "class" nbFontHeading2Class $ text "Members" el @ att "class" nbFontHeading2Class $ text "Members"
el @ att "class" nbBoxClass @ att "style" "margin-bottom:1.5rem" $ mapM_ memberRow mems el @ att "class" nbBoxClass @ att "style" "margin-bottom:1.5rem" $ mapM_ memberRow mems
el @ att "class" nbFontHeading2Class $ text "Invite Members" el @ att "class" nbFontHeading2Class $ text "Invite Members"
@@ -91,7 +92,7 @@ memberRow m = do
el @ att "style" "font-weight:500" $ text (membershipDisplayName m) el @ att "style" "font-weight:500" $ text (membershipDisplayName m)
el @ att "style" "opacity:0.5" $ text (membershipEmail m) el @ att "style" "opacity:0.5" $ text (membershipEmail m)
el @ att "class" nbBadgeClass @ att "style" (if membershipRole m == OwnerRole then "background:var(--nb-yellow);color:#000" else "") $ el @ att "class" nbBadgeClass @ att "style" (if membershipRole m == OwnerRole then "background:var(--nb-yellow);color:#000" else "") $
text (T.pack (show (membershipRole m)) text (T.pack (show (membershipRole m)))
inviteRow :: Invite -> View HouseholdPage () inviteRow :: Invite -> View HouseholdPage ()
inviteRow i = do inviteRow i = do
@@ -105,19 +106,19 @@ initials name =
let ws = T.words name let ws = T.words name
in T.toUpper (T.take 2 (T.concat (map (T.take 1) ws))) in T.toUpper (T.take 2 (T.concat (map (T.take 1) ws)))
page :: (Hyperbole :> es, DB :> es) => Eff es (View HouseholdPage ()) page :: (Hyperbole :> es, DB :> es, IOE :> es) => Page es '[HouseholdPage]
page = do page = do
mSession <- lookupSession @UserSession mSession <- lookupSession @UserSession
case mSession of case mSession of
Nothing -> do Nothing -> do
redirect (routeUri RouteLogin) redirect (routeUri RouteLogin)
pure (el "Redirecting...") pure $ hyper HouseholdPage $ el "Redirecting..."
Just us -> do Just us -> do
hhs <- getUserHouseholds (UserId (usUserId us)) hhs <- getUserHouseholds (UserId (usUserId us))
case hhs of case hhs of
[] -> pure noHouseholdView [] -> pure $ hyper HouseholdPage $ noHouseholdView
(h : _) -> do (h : _) -> do
let hid = unHouseholdId (householdId h) let hid = unHouseholdId (householdId h)
mems <- getMembers hid mems <- getMembers hid
invs <- getInvites hid invs <- getInvites hid
pure (householdView h mems invs) pure $ hyper HouseholdPage $ householdView h mems invs
+5 -5
View File
@@ -32,9 +32,9 @@ instance (DB :> es, IOE :> es) => HyperView LoginPage es where
| verifyPassword (lfPassword formData') (userPasswordHash u) -> do | verifyPassword (lfPassword formData') (userPasswordHash u) -> do
saveSession (UserSession (unUserId (userId u))) saveSession (UserSession (unUserId (userId u)))
redirect (routeUri RouteDashboard) redirect (routeUri RouteDashboard)
pure (el "Redirecting...") pure $ hyper LoginPage $ el "Redirecting..."
_ -> pure (loginView (Just "Invalid email or password")) _ -> pure (loginView (Just "Invalid email or password"))
update Noop = pure (loginView Nothing) update Noop = pure $ hyper LoginPage $ loginView Nothing
loginView :: Maybe Text -> View LoginPage () loginView :: Maybe Text -> View LoginPage ()
loginView mError = do loginView mError = do
el @ att "class" nbContainerClass @ att "style" "max-width:480px;margin:4rem auto" $ do el @ att "class" nbContainerClass @ att "style" "max-width:480px;margin:4rem auto" $ do
@@ -55,11 +55,11 @@ loginView mError = do
text "Remember me" text "Remember me"
submit (text "Log In") @ att "class" nbButtonClass @ att "style" "width:100%" submit (text "Log In") @ att "class" nbButtonClass @ att "style" "width:100%"
route RouteSignup $ text "Don't have an account? Sign Up" route RouteSignup $ text "Don't have an account? Sign Up"
page :: (Hyperbole :> es, DB :> es) => Eff es (View LoginPage ()) page :: (Hyperbole :> es, DB :> es, IOE :> es) => Page es '[LoginPage]
page = do page = do
mSession <- lookupSession @UserSession mSession <- lookupSession @UserSession
case mSession of case mSession of
Just _ -> do Just _ -> do
redirect (routeUri RouteDashboard) redirect (routeUri RouteDashboard)
pure (el "Redirecting...") pure $ hyper LoginPage $ el "Redirecting..."
Nothing -> pure (loginView Nothing) Nothing -> pure $ hyper LoginPage $ loginView Nothing
+4 -4
View File
@@ -40,7 +40,7 @@ instance (DB :> es, IOE :> es) => HyperView SignupPage es where
uid <- createUser (sfDisplayName form) (sfEmail form) pwHash uid <- createUser (sfDisplayName form) (sfEmail form) pwHash
saveSession (UserSession (unUserId uid)) saveSession (UserSession (unUserId uid))
redirect (routeUri RouteDashboard) redirect (routeUri RouteDashboard)
pure (el "Redirecting...") pure $ hyper SignupPage $ el "Redirecting..."
signupView :: Maybe Text -> View SignupPage () signupView :: Maybe Text -> View SignupPage ()
signupView mError = do signupView mError = do
el @ att "class" nbContainerClass @ att "style" "max-width:480px;margin:4rem auto" $ do el @ att "class" nbContainerClass @ att "style" "max-width:480px;margin:4rem auto" $ do
@@ -65,11 +65,11 @@ signupView mError = do
text "I agree to the terms of service" text "I agree to the terms of service"
submit (text "Sign Up") @ att "class" nbButtonClass @ att "style" "width:100%" submit (text "Sign Up") @ att "class" nbButtonClass @ att "style" "width:100%"
route RouteLogin $ text "Already have an account? Log In" route RouteLogin $ text "Already have an account? Log In"
page :: (Hyperbole :> es, DB :> es) => Eff es (View SignupPage ()) page :: (Hyperbole :> es, DB :> es, IOE :> es) => Page es '[SignupPage]
page = do page = do
mSession <- lookupSession @UserSession mSession <- lookupSession @UserSession
case mSession of case mSession of
Just _ -> do Just _ -> do
redirect (routeUri RouteDashboard) redirect (routeUri RouteDashboard)
pure (el "Redirecting...") pure $ hyper SignupPage $ el "Redirecting..."
Nothing -> pure (signupView Nothing) Nothing -> pure $ hyper SignupPage $ signupView Nothing