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
|
||||
|
||||
- **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.
|
||||
- **Module qualifiers:** Use qualified imports with descriptive aliases
|
||||
(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
|
||||
serverside web framework. All HTML rendered in Haskell.
|
||||
- **CSS:** [Neo Brutalism](https://unpkg.com/neobrutalismcss@latest) CDN +
|
||||
`frontend/static/style.css` for custom styles.
|
||||
- **CSS:** [Neo Brutalism](https://github.com/matifandy8/NeoBrutalismCSS) CDN via jsdelivr + `frontend/static/style.css` for custom styles.
|
||||
- **Build:** No npm/build step for frontend. All pages rendered in Haskell.
|
||||
- **Interactive components:** HyperViews with typed Actions and server-side
|
||||
updates via VirtualDOM over WebSocket.
|
||||
@@ -86,5 +90,6 @@ sis/
|
||||
- Run `./scripts/test` before committing. Tests must pass.
|
||||
|
||||
## Agent Autonomy
|
||||
- Run `./hs hlint app/ src/ test/` and fix ALL hints before committing.
|
||||
- As changes are completed then verify functionality using Playwright
|
||||
- Once functionality is confirmed then commit and push changes
|
||||
|
||||
@@ -17,7 +17,7 @@ import Effectful
|
||||
|
||||
import Sis.Database
|
||||
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.View.Layout
|
||||
import Web.Hyperbole
|
||||
|
||||
@@ -18,7 +18,7 @@ import Effectful
|
||||
|
||||
import Sis.Database
|
||||
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.View.Layout
|
||||
import Web.Hyperbole
|
||||
@@ -86,7 +86,7 @@ scheduleLabel ScheduleSometime = "Sometime"
|
||||
scheduleLabel (ScheduleOneOff d _) = "One-off on " <> T.pack (show d)
|
||||
scheduleLabel (ScheduleRecurring p _ mt _ _) =
|
||||
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
|
||||
|
||||
page :: (Hyperbole :> es, DB :> es, IOE :> es) => Page es '[ChoresPage]
|
||||
|
||||
@@ -19,7 +19,7 @@ import Effectful
|
||||
|
||||
import Sis.Database
|
||||
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.View.Layout
|
||||
import Web.Hyperbole
|
||||
|
||||
@@ -17,7 +17,7 @@ import Effectful
|
||||
|
||||
import Sis.Database
|
||||
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.View.Layout
|
||||
import Web.Hyperbole
|
||||
@@ -43,7 +43,7 @@ instance (DB :> es, IOE :> es) => HyperView HouseholdPage es where
|
||||
Just us -> do
|
||||
hhs <- getUserHouseholds (UserId (usUserId us))
|
||||
case hhs of
|
||||
[] -> pure (noHouseholdView)
|
||||
[] -> pure noHouseholdView
|
||||
(h : _) -> do
|
||||
let hid = unHouseholdId (householdId h)
|
||||
mems <- getMembers hid
|
||||
@@ -123,7 +123,7 @@ page = do
|
||||
Just us -> do
|
||||
hhs <- getUserHouseholds (UserId (usUserId us))
|
||||
case hhs of
|
||||
[] -> pure $ hyper HouseholdPage $ noHouseholdView
|
||||
[] -> pure $ hyper HouseholdPage noHouseholdView
|
||||
(h : _) -> do
|
||||
let hid = unHouseholdId (householdId h)
|
||||
mems <- getMembers hid
|
||||
|
||||
@@ -17,7 +17,7 @@ import Effectful
|
||||
import Sis.Auth (generateToken, hashPassword, verifyPassword)
|
||||
import Sis.Database
|
||||
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.View.Layout
|
||||
import Web.Hyperbole
|
||||
@@ -41,7 +41,7 @@ instance (DB :> es, IOE :> es) => HyperView LoginPage es where
|
||||
Just u
|
||||
| verifyPassword (lfPassword formData') (userPasswordHash u) -> do
|
||||
saveSession (UserSession (unUserId (userId u)))
|
||||
pure (loginSuccessView)
|
||||
pure loginSuccessView
|
||||
_ -> pure (loginView (Just "Invalid email or password"))
|
||||
update Noop = pure (loginView Nothing)
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import Effectful
|
||||
import Sis.Auth (generateToken, hashPassword, verifyPassword)
|
||||
import Sis.Database
|
||||
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.View.Layout
|
||||
import Web.Hyperbole
|
||||
@@ -49,7 +49,7 @@ instance (DB :> es, IOE :> es) => HyperView SignupPage es where
|
||||
pwHash <- liftIO (hashPassword (sfPassword form))
|
||||
uid <- createUser (sfDisplayName form) (sfEmail form) pwHash
|
||||
saveSession (UserSession (unUserId uid))
|
||||
pure (signupSuccessView)
|
||||
pure signupSuccessView
|
||||
|
||||
signupSuccessView :: View SignupPage ()
|
||||
signupSuccessView = do
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{-# LANGUAGE DeriveGeneric #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
module Sis.Route (AppRoute (..)) where
|
||||
|
||||
|
||||
+17
-7
@@ -1,12 +1,22 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
-- | Neo Brutalism CSS class name constants for Hyperbole views.
|
||||
-- Based on https://github.com/matifandy8/NeoBrutalismCSS
|
||||
module Sis.Style
|
||||
( nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass
|
||||
, nbBadgeClass, nbHeadingClass
|
||||
, nbNavbarClass, nbNavbarLinkClass, nbContainerClass, nbListItemClass
|
||||
, colorRed, colorYellow, colorGreen
|
||||
{- | Neo Brutalism CSS class name constants for Hyperbole views.
|
||||
Based on https://github.com/matifandy8/NeoBrutalismCSS
|
||||
-}
|
||||
module Sis.Style (
|
||||
nbBoxClass,
|
||||
nbButtonDefaultClass,
|
||||
nbInputClass,
|
||||
nbLabelClass,
|
||||
nbBadgeClass,
|
||||
nbHeadingClass,
|
||||
nbNavbarClass,
|
||||
nbNavbarLinkClass,
|
||||
nbContainerClass,
|
||||
nbListItemClass,
|
||||
colorRed,
|
||||
colorYellow,
|
||||
colorGreen,
|
||||
) where
|
||||
|
||||
import Data.Text (Text)
|
||||
|
||||
+3
-2
@@ -292,7 +292,8 @@ data ActivityFormData = ActivityFormData
|
||||
}
|
||||
deriving (Show, Eq, Generic, FromForm)
|
||||
|
||||
data HouseholdFormData = HouseholdFormData
|
||||
newtype HouseholdFormData = HouseholdFormData
|
||||
{ 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 Sis.Route
|
||||
import Sis.Style (nbNavbarClass, nbNavbarLinkClass, nbHeadingClass, nbButtonDefaultClass)
|
||||
import Sis.Style (nbButtonDefaultClass, nbHeadingClass, nbNavbarClass, nbNavbarLinkClass)
|
||||
import Web.Hyperbole
|
||||
import Web.Hyperbole.Effect.Session
|
||||
|
||||
@@ -23,7 +23,7 @@ import Web.Hyperbole.Effect.Session
|
||||
-- Session
|
||||
----------------------------------------------------------------------
|
||||
|
||||
data UserSession = UserSession
|
||||
newtype UserSession = UserSession
|
||||
{ usUserId :: Int
|
||||
}
|
||||
deriving stock (Show, Eq, Generic)
|
||||
|
||||
Reference in New Issue
Block a user