chore: fix all hlint warnings, update AGENTS.md pre-commit checklist
- Fix 8 hlint hints across 6 files (unused pragma, newtype, lambda, redundant brackets/\$) - Add blank line after LANGUAGE pragma in Route.hs (fourmolu) - Fix HouseholdFormData deriving to use explicit strategies for newtype - Update AGENTS.md: require hlint clean before every commit, add pre-commit checklist (format, lint, build, test), update NB CSS URL to jsdelivr CDN
This commit is contained in:
@@ -21,7 +21,12 @@ Hyperbole, a serverside web framework. There is zero application JavaScript.
|
|||||||
## Haskell Conventions
|
## Haskell Conventions
|
||||||
|
|
||||||
- **Style:** fourmolu-formatted. Run `./hs fourmolu --mode inplace app/ src/ test/` before committing.
|
- **Style:** fourmolu-formatted. Run `./hs fourmolu --mode inplace app/ src/ test/` before committing.
|
||||||
- **Lint:** hlint clean required. Fix any hints before committing.
|
- **Lint:** hlint clean required — MUST run `./hs hlint app/ src/ test/` and fix all hints before committing. Zero hints is the standard. Do not suppress or ignore hlint suggestions.
|
||||||
|
- **Pre-commit checklist:** Before every commit, run:
|
||||||
|
1. `./hs fourmolu --mode inplace app/ src/ test/` — format code
|
||||||
|
2. `./hs hlint app/ src/ test/` — fix ALL hints (must output "No hints")
|
||||||
|
3. `./hs stack build --fast` — must compile with zero errors
|
||||||
|
4. `./hs stack test --fast` — all tests must pass
|
||||||
- **Warnings:** `-Wall -Werror` in `package.yaml`. All warnings are fatal.
|
- **Warnings:** `-Wall -Werror` in `package.yaml`. All warnings are fatal.
|
||||||
- **Module qualifiers:** Use qualified imports with descriptive aliases
|
- **Module qualifiers:** Use qualified imports with descriptive aliases
|
||||||
(e.g., `import Data.Text qualified as T`).
|
(e.g., `import Data.Text qualified as T`).
|
||||||
@@ -37,8 +42,7 @@ Hyperbole, a serverside web framework. There is zero application JavaScript.
|
|||||||
|
|
||||||
- **Framework:** [Hyperbole](https://github.com/seanhess/hyperbole) — Haskell
|
- **Framework:** [Hyperbole](https://github.com/seanhess/hyperbole) — Haskell
|
||||||
serverside web framework. All HTML rendered in Haskell.
|
serverside web framework. All HTML rendered in Haskell.
|
||||||
- **CSS:** [Neo Brutalism](https://unpkg.com/neobrutalismcss@latest) CDN +
|
- **CSS:** [Neo Brutalism](https://github.com/matifandy8/NeoBrutalismCSS) CDN via jsdelivr + `frontend/static/style.css` for custom styles.
|
||||||
`frontend/static/style.css` for custom styles.
|
|
||||||
- **Build:** No npm/build step for frontend. All pages rendered in Haskell.
|
- **Build:** No npm/build step for frontend. All pages rendered in Haskell.
|
||||||
- **Interactive components:** HyperViews with typed Actions and server-side
|
- **Interactive components:** HyperViews with typed Actions and server-side
|
||||||
updates via VirtualDOM over WebSocket.
|
updates via VirtualDOM over WebSocket.
|
||||||
@@ -86,5 +90,6 @@ sis/
|
|||||||
- Run `./scripts/test` before committing. Tests must pass.
|
- Run `./scripts/test` before committing. Tests must pass.
|
||||||
|
|
||||||
## Agent Autonomy
|
## Agent Autonomy
|
||||||
|
- Run `./hs hlint app/ src/ test/` and fix ALL hints before committing.
|
||||||
- As changes are completed then verify functionality using Playwright
|
- As changes are completed then verify functionality using Playwright
|
||||||
- Once functionality is confirmed then commit and push changes
|
- Once functionality is confirmed then commit and push changes
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import Effectful
|
|||||||
|
|
||||||
import Sis.Database
|
import Sis.Database
|
||||||
import Sis.Route
|
import Sis.Route
|
||||||
import Sis.Style (nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass, nbBadgeClass, nbHeadingClass, nbContainerClass, nbListItemClass, colorRed, colorYellow, colorGreen)
|
import Sis.Style (colorGreen, colorRed, colorYellow, nbBadgeClass, nbBoxClass, nbButtonDefaultClass, nbContainerClass, nbHeadingClass, nbInputClass, nbLabelClass, nbListItemClass)
|
||||||
import Sis.Types
|
import Sis.Types
|
||||||
import Sis.View.Layout
|
import Sis.View.Layout
|
||||||
import Web.Hyperbole
|
import Web.Hyperbole
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import Effectful
|
|||||||
|
|
||||||
import Sis.Database
|
import Sis.Database
|
||||||
import Sis.Route
|
import Sis.Route
|
||||||
import Sis.Style (nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass, nbBadgeClass, nbHeadingClass, nbContainerClass, nbListItemClass, colorRed, colorYellow, colorGreen)
|
import Sis.Style (colorGreen, colorRed, colorYellow, nbBadgeClass, nbBoxClass, nbButtonDefaultClass, nbContainerClass, nbHeadingClass, nbInputClass, nbLabelClass, nbListItemClass)
|
||||||
import Sis.Types
|
import Sis.Types
|
||||||
import Sis.View.Layout
|
import Sis.View.Layout
|
||||||
import Web.Hyperbole
|
import Web.Hyperbole
|
||||||
@@ -86,7 +86,7 @@ scheduleLabel ScheduleSometime = "Sometime"
|
|||||||
scheduleLabel (ScheduleOneOff d _) = "One-off on " <> T.pack (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 "" (" at " <>) mt
|
||||||
in "Recurs " <> pText <> timePart
|
in "Recurs " <> pText <> timePart
|
||||||
|
|
||||||
page :: (Hyperbole :> es, DB :> es, IOE :> es) => Page es '[ChoresPage]
|
page :: (Hyperbole :> es, DB :> es, IOE :> es) => Page es '[ChoresPage]
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import Effectful
|
|||||||
|
|
||||||
import Sis.Database
|
import Sis.Database
|
||||||
import Sis.Route
|
import Sis.Route
|
||||||
import Sis.Style (nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass, nbBadgeClass, nbHeadingClass, nbContainerClass, nbListItemClass, colorRed, colorYellow, colorGreen)
|
import Sis.Style (colorGreen, colorRed, colorYellow, nbBadgeClass, nbBoxClass, nbButtonDefaultClass, nbContainerClass, nbHeadingClass, nbInputClass, nbLabelClass, nbListItemClass)
|
||||||
import Sis.Types
|
import Sis.Types
|
||||||
import Sis.View.Layout
|
import Sis.View.Layout
|
||||||
import Web.Hyperbole
|
import Web.Hyperbole
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import Effectful
|
|||||||
|
|
||||||
import Sis.Database
|
import Sis.Database
|
||||||
import Sis.Route
|
import Sis.Route
|
||||||
import Sis.Style (nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass, nbBadgeClass, nbHeadingClass, nbContainerClass, nbListItemClass, colorRed, colorYellow, colorGreen)
|
import Sis.Style (colorGreen, colorRed, colorYellow, nbBadgeClass, nbBoxClass, nbButtonDefaultClass, nbContainerClass, nbHeadingClass, nbInputClass, nbLabelClass, nbListItemClass)
|
||||||
import Sis.Types
|
import Sis.Types
|
||||||
import Sis.View.Layout
|
import Sis.View.Layout
|
||||||
import Web.Hyperbole
|
import Web.Hyperbole
|
||||||
@@ -43,7 +43,7 @@ instance (DB :> es, IOE :> es) => HyperView HouseholdPage 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 (noHouseholdView)
|
[] -> pure noHouseholdView
|
||||||
(h : _) -> do
|
(h : _) -> do
|
||||||
let hid = unHouseholdId (householdId h)
|
let hid = unHouseholdId (householdId h)
|
||||||
mems <- getMembers hid
|
mems <- getMembers hid
|
||||||
@@ -123,7 +123,7 @@ page = do
|
|||||||
Just us -> do
|
Just us -> do
|
||||||
hhs <- getUserHouseholds (UserId (usUserId us))
|
hhs <- getUserHouseholds (UserId (usUserId us))
|
||||||
case hhs of
|
case hhs of
|
||||||
[] -> pure $ hyper HouseholdPage $ 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
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import Effectful
|
|||||||
import Sis.Auth (generateToken, hashPassword, verifyPassword)
|
import Sis.Auth (generateToken, hashPassword, verifyPassword)
|
||||||
import Sis.Database
|
import Sis.Database
|
||||||
import Sis.Route
|
import Sis.Route
|
||||||
import Sis.Style (nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass, nbBadgeClass, nbHeadingClass, nbContainerClass, nbListItemClass, colorRed, colorYellow, colorGreen)
|
import Sis.Style (colorGreen, colorRed, colorYellow, nbBadgeClass, nbBoxClass, nbButtonDefaultClass, nbContainerClass, nbHeadingClass, nbInputClass, nbLabelClass, nbListItemClass)
|
||||||
import Sis.Types
|
import Sis.Types
|
||||||
import Sis.View.Layout
|
import Sis.View.Layout
|
||||||
import Web.Hyperbole
|
import Web.Hyperbole
|
||||||
@@ -41,7 +41,7 @@ instance (DB :> es, IOE :> es) => HyperView LoginPage es where
|
|||||||
Just u
|
Just u
|
||||||
| verifyPassword (lfPassword formData') (userPasswordHash u) -> do
|
| verifyPassword (lfPassword formData') (userPasswordHash u) -> do
|
||||||
saveSession (UserSession (unUserId (userId u)))
|
saveSession (UserSession (unUserId (userId u)))
|
||||||
pure (loginSuccessView)
|
pure loginSuccessView
|
||||||
_ -> pure (loginView (Just "Invalid email or password"))
|
_ -> pure (loginView (Just "Invalid email or password"))
|
||||||
update Noop = pure (loginView Nothing)
|
update Noop = pure (loginView Nothing)
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import Effectful
|
|||||||
import Sis.Auth (generateToken, hashPassword, verifyPassword)
|
import Sis.Auth (generateToken, hashPassword, verifyPassword)
|
||||||
import Sis.Database
|
import Sis.Database
|
||||||
import Sis.Route
|
import Sis.Route
|
||||||
import Sis.Style (nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass, nbBadgeClass, nbHeadingClass, nbContainerClass, nbListItemClass, colorRed, colorYellow, colorGreen)
|
import Sis.Style (colorGreen, colorRed, colorYellow, nbBadgeClass, nbBoxClass, nbButtonDefaultClass, nbContainerClass, nbHeadingClass, nbInputClass, nbLabelClass, nbListItemClass)
|
||||||
import Sis.Types
|
import Sis.Types
|
||||||
import Sis.View.Layout
|
import Sis.View.Layout
|
||||||
import Web.Hyperbole
|
import Web.Hyperbole
|
||||||
@@ -49,7 +49,7 @@ instance (DB :> es, IOE :> es) => HyperView SignupPage es where
|
|||||||
pwHash <- liftIO (hashPassword (sfPassword form))
|
pwHash <- liftIO (hashPassword (sfPassword form))
|
||||||
uid <- createUser (sfDisplayName form) (sfEmail form) pwHash
|
uid <- createUser (sfDisplayName form) (sfEmail form) pwHash
|
||||||
saveSession (UserSession (unUserId uid))
|
saveSession (UserSession (unUserId uid))
|
||||||
pure (signupSuccessView)
|
pure signupSuccessView
|
||||||
|
|
||||||
signupSuccessView :: View SignupPage ()
|
signupSuccessView :: View SignupPage ()
|
||||||
signupSuccessView = do
|
signupSuccessView = do
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{-# LANGUAGE DeriveGeneric #-}
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
|
|
||||||
module Sis.Route (AppRoute (..)) where
|
module Sis.Route (AppRoute (..)) where
|
||||||
|
|
||||||
|
|||||||
+17
-7
@@ -1,12 +1,22 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
-- | Neo Brutalism CSS class name constants for Hyperbole views.
|
{- | Neo Brutalism CSS class name constants for Hyperbole views.
|
||||||
-- Based on https://github.com/matifandy8/NeoBrutalismCSS
|
Based on https://github.com/matifandy8/NeoBrutalismCSS
|
||||||
module Sis.Style
|
-}
|
||||||
( nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass
|
module Sis.Style (
|
||||||
, nbBadgeClass, nbHeadingClass
|
nbBoxClass,
|
||||||
, nbNavbarClass, nbNavbarLinkClass, nbContainerClass, nbListItemClass
|
nbButtonDefaultClass,
|
||||||
, colorRed, colorYellow, colorGreen
|
nbInputClass,
|
||||||
|
nbLabelClass,
|
||||||
|
nbBadgeClass,
|
||||||
|
nbHeadingClass,
|
||||||
|
nbNavbarClass,
|
||||||
|
nbNavbarLinkClass,
|
||||||
|
nbContainerClass,
|
||||||
|
nbListItemClass,
|
||||||
|
colorRed,
|
||||||
|
colorYellow,
|
||||||
|
colorGreen,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
|
|||||||
+3
-2
@@ -292,7 +292,8 @@ data ActivityFormData = ActivityFormData
|
|||||||
}
|
}
|
||||||
deriving (Show, Eq, Generic, FromForm)
|
deriving (Show, Eq, Generic, FromForm)
|
||||||
|
|
||||||
data HouseholdFormData = HouseholdFormData
|
newtype HouseholdFormData = HouseholdFormData
|
||||||
{ hfdName :: Text
|
{ hfdName :: Text
|
||||||
}
|
}
|
||||||
deriving (Show, Eq, Generic, FromForm)
|
deriving stock (Show, Eq, Generic)
|
||||||
|
deriving anyclass (FromForm)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import Data.Text (Text)
|
|||||||
import GHC.Generics (Generic)
|
import GHC.Generics (Generic)
|
||||||
|
|
||||||
import Sis.Route
|
import Sis.Route
|
||||||
import Sis.Style (nbNavbarClass, nbNavbarLinkClass, nbHeadingClass, nbButtonDefaultClass)
|
import Sis.Style (nbButtonDefaultClass, nbHeadingClass, nbNavbarClass, nbNavbarLinkClass)
|
||||||
import Web.Hyperbole
|
import Web.Hyperbole
|
||||||
import Web.Hyperbole.Effect.Session
|
import Web.Hyperbole.Effect.Session
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ import Web.Hyperbole.Effect.Session
|
|||||||
-- Session
|
-- Session
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
data UserSession = UserSession
|
newtype UserSession = UserSession
|
||||||
{ usUserId :: Int
|
{ usUserId :: Int
|
||||||
}
|
}
|
||||||
deriving stock (Show, Eq, Generic)
|
deriving stock (Show, Eq, Generic)
|
||||||
|
|||||||
Reference in New Issue
Block a user