fix: update to correct NeoBrutalismCSS CDN and class names
- Switch CDN to jsdelivr (matches design spec) - Use nb-card, nb-card-title, nb-button default, nb-checkbox, nb-navbar-link - Add Google Fonts import for Lexend Mega - Style all route links as nb-button default buttons - Fix navbar to use nb-navbar-brand and nb-navbar-nav - Update style.css for new NB version compatibility
This commit is contained in:
+16
-13
@@ -16,42 +16,45 @@ body {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Override NB navbar to have drop shadow like our design */
|
||||||
.nb-navbar {
|
.nb-navbar {
|
||||||
background: #fff;
|
|
||||||
border-bottom: 3px solid #000;
|
|
||||||
padding: 0.75rem 1.5rem;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
box-shadow: 4px 4px 0 #000;
|
box-shadow: 4px 4px 0 #000;
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nb-navbar-start, .nb-navbar-end {
|
/* List items with subtle dividers */
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nb-list-item {
|
.nb-list-item {
|
||||||
border-bottom: 1px solid rgba(0,0,0,0.1);
|
border-bottom: 1px solid rgba(0,0,0,0.1);
|
||||||
|
padding: 0.5rem;
|
||||||
}
|
}
|
||||||
.nb-list-item:last-child {
|
.nb-list-item:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Stat tiles */
|
||||||
|
.stat-tile {
|
||||||
|
text-align: center;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Container padding */
|
||||||
|
.nb-container {
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes fadeIn {
|
@keyframes fadeIn {
|
||||||
from { opacity: 0; }
|
from { opacity: 0; }
|
||||||
to { opacity: 1; }
|
to { opacity: 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.nb-navbar {
|
.nb-navbar {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
}
|
}
|
||||||
.nb-navbar-end {
|
.nb-navbar-nav {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import Effectful
|
|||||||
|
|
||||||
import Sis.Database
|
import Sis.Database
|
||||||
import Sis.Route
|
import Sis.Route
|
||||||
import Sis.Style
|
import Sis.Style (nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass, nbBadgeClass, nbHeadingClass, nbContainerClass, nbListItemClass, colorRed, colorYellow, colorGreen)
|
||||||
import Sis.Types
|
import Sis.Types
|
||||||
import Sis.View.Layout
|
import Sis.View.Layout
|
||||||
import Web.Hyperbole
|
import Web.Hyperbole
|
||||||
@@ -52,7 +52,7 @@ instance (DB :> es, IOE :> es) => HyperView ActivityPage es where
|
|||||||
activityView :: ActivityLogPage -> View ActivityPage ()
|
activityView :: ActivityLogPage -> View ActivityPage ()
|
||||||
activityView log = do
|
activityView log = 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 "Activity Log"
|
el @ att "class" nbHeadingClass $ text "Activity Log"
|
||||||
if null (alpEntries log)
|
if null (alpEntries log)
|
||||||
then el @ att "style" "opacity:0.5" $ text "No activity recorded yet."
|
then el @ att "style" "opacity:0.5" $ text "No activity recorded yet."
|
||||||
else el @ att "class" nbBoxClass $ mapM_ entryRow (alpEntries log)
|
else el @ att "class" nbBoxClass $ mapM_ entryRow (alpEntries log)
|
||||||
@@ -60,12 +60,12 @@ activityView log = do
|
|||||||
then el @ att "style" "margin-top:1rem;display:flex;gap:0.5rem;justify-content:center" $ do
|
then el @ att "style" "margin-top:1rem;display:flex;gap:0.5rem;justify-content:center" $ do
|
||||||
let totalPages = (alpTotal log + alpPerPage log - 1) `div` alpPerPage log
|
let totalPages = (alpTotal log + alpPerPage log - 1) `div` alpPerPage log
|
||||||
if alpPage log > 1
|
if alpPage log > 1
|
||||||
then button (GoToPage (alpPage log - 1)) @ att "class" nbButtonClass $ text "Previous"
|
then button (GoToPage (alpPage log - 1)) @ att "class" nbButtonDefaultClass $ text "Previous"
|
||||||
else none
|
else none
|
||||||
el @ att "style" "align-self:center" $
|
el @ att "style" "align-self:center" $
|
||||||
text ("Page " <> T.pack (show (alpPage log)) <> " of " <> T.pack (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" nbButtonDefaultClass $ text "Next"
|
||||||
else none
|
else none
|
||||||
else none
|
else none
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import Effectful
|
|||||||
|
|
||||||
import Sis.Database
|
import Sis.Database
|
||||||
import Sis.Route
|
import Sis.Route
|
||||||
import Sis.Style
|
import Sis.Style (nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass, nbBadgeClass, nbHeadingClass, nbContainerClass, nbListItemClass, colorRed, colorYellow, colorGreen)
|
||||||
import Sis.Types
|
import Sis.Types
|
||||||
import Sis.View.Layout
|
import Sis.View.Layout
|
||||||
import Web.Hyperbole
|
import Web.Hyperbole
|
||||||
@@ -59,8 +59,8 @@ choresView :: [Chore] -> View ChoresPage ()
|
|||||||
choresView chores' = do
|
choresView chores' = 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 "style" "display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem" $ do
|
el @ att "style" "display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem" $ do
|
||||||
el @ att "class" nbFontHeading1Class $ text "Chores"
|
el @ att "class" nbHeadingClass $ text "Chores"
|
||||||
button CNewChore @ att "class" nbButtonClass $ text "+ New Chore"
|
button CNewChore @ att "class" nbButtonDefaultClass $ text "+ New Chore"
|
||||||
if null chores'
|
if null chores'
|
||||||
then el @ att "style" "opacity:0.5" $ text "No chores yet. Create one to get started!"
|
then el @ att "style" "opacity:0.5" $ text "No chores yet. Create one to get started!"
|
||||||
else el @ att "class" nbBoxClass $ mapM_ choreRow chores'
|
else el @ att "class" nbBoxClass $ mapM_ choreRow chores'
|
||||||
@@ -73,8 +73,8 @@ choreRow c = do
|
|||||||
el @ att "style" "font-weight:500" $ text (choreName c)
|
el @ att "style" "font-weight:500" $ text (choreName c)
|
||||||
el @ att "style" "opacity:0.5;font-size:0.85rem" $ text (scheduleLabel (choreSchedule c))
|
el @ att "style" "opacity:0.5;font-size:0.85rem" $ text (scheduleLabel (choreSchedule c))
|
||||||
el @ att "style" "display:flex;gap:0.25rem" $ do
|
el @ att "style" "display:flex;gap:0.25rem" $ do
|
||||||
button CNewChore @ att "class" nbButtonClass @ att "style" "font-size:0.8rem;padding:0.25rem 0.5rem" $ text "Edit"
|
button CNewChore @ att "class" nbButtonDefaultClass @ att "style" "font-size:0.8rem;padding:0.25rem 0.5rem" $ text "Edit"
|
||||||
button (CDeleteChore (choreId c)) @ att "class" nbButtonClass @ att "style" "font-size:0.8rem;padding:0.25rem 0.5rem;background:var(--nb-red)" $ text "Delete"
|
button (CDeleteChore (choreId c)) @ att "class" nbButtonDefaultClass @ att "style" "font-size:0.8rem;padding:0.25rem 0.5rem;background:var(--nb-red)" $ text "Delete"
|
||||||
|
|
||||||
scheduleBadge :: Schedule -> Text
|
scheduleBadge :: Schedule -> Text
|
||||||
scheduleBadge ScheduleSometime{} = "sometime"
|
scheduleBadge ScheduleSometime{} = "sometime"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import Effectful
|
|||||||
|
|
||||||
import Sis.Database
|
import Sis.Database
|
||||||
import Sis.Route
|
import Sis.Route
|
||||||
import Sis.Style
|
import Sis.Style (nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass, nbBadgeClass, nbHeadingClass, nbContainerClass, nbListItemClass, colorRed, colorYellow, colorGreen)
|
||||||
import Sis.Types
|
import Sis.Types
|
||||||
import Sis.View.Layout
|
import Sis.View.Layout
|
||||||
import Web.Hyperbole
|
import Web.Hyperbole
|
||||||
@@ -56,27 +56,27 @@ instance (DB :> es, IOE :> es) => HyperView DashboardPage es where
|
|||||||
dashboardView :: Dashboard -> View DashboardPage ()
|
dashboardView :: Dashboard -> View DashboardPage ()
|
||||||
dashboardView dash = do
|
dashboardView dash = 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 "Dashboard"
|
el @ att "class" nbHeadingClass $ 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" (T.pack (show (dsOverdue stats))) colorRed
|
statTile "Overdue" (T.pack (show (dsOverdue stats))) colorRed
|
||||||
statTile "Due Today" (T.pack (show (dsDueToday stats))) colorYellow
|
statTile "Due Today" (T.pack (show (dsDueToday stats))) colorYellow
|
||||||
statTile "Done This Week" (T.pack (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" nbHeadingClass $ 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."
|
||||||
else el @ att "class" nbBoxClass $ do
|
else el @ att "class" nbBoxClass $ do
|
||||||
mapM_ dueItemRow (dashDueItems dash)
|
mapM_ dueItemRow (dashDueItems dash)
|
||||||
el @ att "class" nbFontHeading2Class $ text "Completed Today"
|
el @ att "class" nbHeadingClass $ text "Completed Today"
|
||||||
if null (dashCompletedItems dash)
|
if null (dashCompletedItems dash)
|
||||||
then el @ att "style" "opacity:0.5" $ text "No activity recorded today."
|
then el @ att "style" "opacity:0.5" $ text "No activity recorded today."
|
||||||
else el @ att "class" nbBoxClass $ mapM_ completedItemRow (dashCompletedItems dash)
|
else el @ att "class" nbBoxClass $ mapM_ completedItemRow (dashCompletedItems dash)
|
||||||
route RActivity $ text "View Full Activity Log"
|
route RActivity @ att "class" nbButtonDefaultClass $ text "View Full Activity Log"
|
||||||
|
|
||||||
statTile :: Text -> Text -> Text -> View ctx ()
|
statTile :: Text -> Text -> Text -> View ctx ()
|
||||||
statTile label count color = do
|
statTile label count color = do
|
||||||
el @ att "class" nbBoxClass @ att "style" ("flex:1;text-align:center;padding:1rem;border-color:" <> color) $ do
|
el @ att "class" nbBoxClass @ att "style" ("flex:1;text-align:center;padding:1rem;border-color:" <> color) $ do
|
||||||
el @ att "class" nbFontHeading1Class $ text count
|
el @ att "class" nbHeadingClass $ text count
|
||||||
text label
|
text label
|
||||||
|
|
||||||
dueItemRow :: DueItem -> View DashboardPage ()
|
dueItemRow :: DueItem -> View DashboardPage ()
|
||||||
@@ -90,7 +90,7 @@ dueItemRow di = do
|
|||||||
case diAssigneeName di of
|
case diAssigneeName di of
|
||||||
Just name -> el @ att "style" "opacity:0.5" $ text ("(" <> name <> ")")
|
Just name -> el @ att "style" "opacity:0.5" $ text ("(" <> name <> ")")
|
||||||
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" nbButtonDefaultClass @ att "style" "font-size:0.85rem" $ text "Check Off"
|
||||||
|
|
||||||
completedItemRow :: CompletedItem -> View DashboardPage ()
|
completedItemRow :: CompletedItem -> View DashboardPage ()
|
||||||
completedItemRow ci = do
|
completedItemRow ci = do
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import Effectful
|
|||||||
|
|
||||||
import Sis.Database
|
import Sis.Database
|
||||||
import Sis.Route
|
import Sis.Route
|
||||||
import Sis.Style
|
import Sis.Style (nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass, nbBadgeClass, nbHeadingClass, nbContainerClass, nbListItemClass, colorRed, colorYellow, colorGreen)
|
||||||
import Sis.Types
|
import Sis.Types
|
||||||
import Sis.View.Layout
|
import Sis.View.Layout
|
||||||
import Web.Hyperbole
|
import Web.Hyperbole
|
||||||
@@ -69,7 +69,7 @@ noHouseholdView :: View HouseholdPage ()
|
|||||||
noHouseholdView = do
|
noHouseholdView = 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
|
||||||
el @ att "class" nbBoxClass @ att "style" "padding:2rem;text-align:center" $ do
|
el @ att "class" nbBoxClass @ att "style" "padding:2rem;text-align:center" $ do
|
||||||
el @ att "class" nbFontHeading1Class $ text "Create Your Household"
|
el @ att "class" nbHeadingClass $ text "Create Your Household"
|
||||||
el @ att "style" "margin-bottom:1rem" $ text "You need a household to get started."
|
el @ att "style" "margin-bottom:1rem" $ text "You need a household to get started."
|
||||||
-- Form for household creation would go here
|
-- Form for household creation would go here
|
||||||
el $ text "Household creation form coming soon"
|
el $ text "Household creation form coming soon"
|
||||||
@@ -77,18 +77,18 @@ noHouseholdView = do
|
|||||||
householdView :: Household -> [Membership] -> [Invite] -> View HouseholdPage ()
|
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" nbHeadingClass $ 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" nbHeadingClass $ 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" nbHeadingClass $ text "Invite Members"
|
||||||
el @ att "class" nbBoxClass $ do
|
el @ att "class" nbBoxClass $ do
|
||||||
el @ att "style" "margin-bottom:0.5rem" $ text "Create an invite link to share:"
|
el @ att "style" "margin-bottom:0.5rem" $ text "Create an invite link to share:"
|
||||||
button CreateInviteAction @ att "class" nbButtonClass $ text "+ Create Invite Link"
|
button CreateInviteAction @ att "class" nbButtonDefaultClass $ text "+ Create Invite Link"
|
||||||
if null invs
|
if null invs
|
||||||
then none
|
then none
|
||||||
else el @ att "style" "margin-top:1rem" $ do
|
else el @ att "style" "margin-top:1rem" $ do
|
||||||
el @ att "class" nbFontHeading2Class $ text "Pending Invites"
|
el @ att "class" nbHeadingClass $ text "Pending Invites"
|
||||||
mapM_ inviteRow (filter ((== InvitePending) . inviteStatus) invs)
|
mapM_ inviteRow (filter ((== InvitePending) . inviteStatus) invs)
|
||||||
|
|
||||||
memberRow :: Membership -> View HouseholdPage ()
|
memberRow :: Membership -> View HouseholdPage ()
|
||||||
@@ -106,7 +106,7 @@ inviteRow :: Invite -> View HouseholdPage ()
|
|||||||
inviteRow i = do
|
inviteRow i = do
|
||||||
el @ att "class" nbListItemClass @ att "style" "display:flex;justify-content:space-between;padding:0.5rem" $ do
|
el @ att "class" nbListItemClass @ att "style" "display:flex;justify-content:space-between;padding:0.5rem" $ do
|
||||||
el @ att "style" "font-size:0.85rem" $ text ("/invite/" <> inviteCode i)
|
el @ att "style" "font-size:0.85rem" $ text ("/invite/" <> inviteCode i)
|
||||||
button (RevokeInviteAction (inviteId i)) @ att "class" nbButtonClass @ att "style" "font-size:0.8rem;background:var(--nb-red)" $
|
button (RevokeInviteAction (inviteId i)) @ att "class" nbButtonDefaultClass @ att "style" "font-size:0.8rem;background:var(--nb-red)" $
|
||||||
text "Revoke"
|
text "Revoke"
|
||||||
|
|
||||||
initials :: Text -> Text
|
initials :: Text -> Text
|
||||||
|
|||||||
@@ -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
|
import Sis.Style (nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass, nbBadgeClass, nbHeadingClass, nbContainerClass, nbListItemClass, colorRed, colorYellow, colorGreen)
|
||||||
import Sis.Types
|
import Sis.Types
|
||||||
import Sis.View.Layout
|
import Sis.View.Layout
|
||||||
import Web.Hyperbole
|
import Web.Hyperbole
|
||||||
@@ -49,15 +49,15 @@ loginSuccessView :: View LoginPage ()
|
|||||||
loginSuccessView = do
|
loginSuccessView = do
|
||||||
el @ att "class" nbContainerClass @ att "style" "max-width:480px;margin:4rem auto;text-align:center" $ do
|
el @ att "class" nbContainerClass @ att "style" "max-width:480px;margin:4rem auto;text-align:center" $ do
|
||||||
el @ att "class" nbBoxClass @ att "style" "padding:2rem" $ do
|
el @ att "class" nbBoxClass @ att "style" "padding:2rem" $ do
|
||||||
el @ att "class" nbFontHeading1Class $ text "Logged In!"
|
el @ att "class" nbHeadingClass $ text "Logged In!"
|
||||||
el @ att "style" "margin-top:1rem;margin-bottom:1rem" $ text "You are now logged in."
|
el @ att "style" "margin-top:1rem;margin-bottom:1rem" $ text "You are now logged in."
|
||||||
route RDashboard $ text "Go to Dashboard"
|
route RDashboard @ att "class" nbButtonDefaultClass $ text "Go to Dashboard"
|
||||||
|
|
||||||
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
|
||||||
el @ att "class" nbBoxClass @ att "style" "padding:2rem" $ do
|
el @ att "class" nbBoxClass @ att "style" "padding:2rem" $ do
|
||||||
el @ att "class" nbFontHeading1Class $ text "Welcome Back"
|
el @ att "class" nbHeadingClass $ text "Welcome Back"
|
||||||
el @ att "style" "opacity:0.7;margin-bottom:1.5rem" $ text "Log in to manage your household chores."
|
el @ att "style" "opacity:0.7;margin-bottom:1.5rem" $ text "Log in to manage your household chores."
|
||||||
case mError of
|
case mError of
|
||||||
Just err ->
|
Just err ->
|
||||||
@@ -69,10 +69,11 @@ loginView mError = do
|
|||||||
el @ att "class" nbLabelClass $ text "Password"
|
el @ att "class" nbLabelClass $ text "Password"
|
||||||
tag "input" @ att "type" "password" . att "name" "lfPassword" . att "class" nbInputClass @ att "style" "width:100%" $ none
|
tag "input" @ att "type" "password" . att "name" "lfPassword" . att "class" nbInputClass @ att "style" "width:100%" $ none
|
||||||
el @ att "style" "display:flex;align-items:center;gap:0.5rem;margin-bottom:1rem" $ do
|
el @ att "style" "display:flex;align-items:center;gap:0.5rem;margin-bottom:1rem" $ do
|
||||||
tag "input" @ att "type" "checkbox" . att "name" "lfRemember" $ none
|
tag "input" @ att "type" "checkbox" . att "name" "lfRemember" . att "class" "nb-checkbox" $ none
|
||||||
text "Remember me"
|
el @ att "class" nbLabelClass $ text "Remember me"
|
||||||
submit (text "Log In") @ att "class" nbButtonClass @ att "style" "width:100%"
|
submit (text "Log In") @ att "class" nbButtonDefaultClass @ att "style" "width:100%"
|
||||||
route RSignup $ text "Don't have an account? Sign Up"
|
el @ att "style" "margin-top:1rem;text-align:center" $ do
|
||||||
|
route RSignup @ att "class" nbButtonDefaultClass $ text "Sign Up"
|
||||||
page :: (Hyperbole :> es, DB :> es, IOE :> es) => Page es '[LoginPage]
|
page :: (Hyperbole :> es, DB :> es, IOE :> es) => Page es '[LoginPage]
|
||||||
page = do
|
page = do
|
||||||
mSession <- lookupSession @UserSession
|
mSession <- lookupSession @UserSession
|
||||||
|
|||||||
@@ -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
|
import Sis.Style (nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass, nbBadgeClass, nbHeadingClass, nbContainerClass, nbListItemClass, colorRed, colorYellow, colorGreen)
|
||||||
import Sis.Types
|
import Sis.Types
|
||||||
import Sis.View.Layout
|
import Sis.View.Layout
|
||||||
import Web.Hyperbole
|
import Web.Hyperbole
|
||||||
@@ -55,15 +55,15 @@ signupSuccessView :: View SignupPage ()
|
|||||||
signupSuccessView = do
|
signupSuccessView = do
|
||||||
el @ att "class" nbContainerClass @ att "style" "max-width:480px;margin:4rem auto;text-align:center" $ do
|
el @ att "class" nbContainerClass @ att "style" "max-width:480px;margin:4rem auto;text-align:center" $ do
|
||||||
el @ att "class" nbBoxClass @ att "style" "padding:2rem" $ do
|
el @ att "class" nbBoxClass @ att "style" "padding:2rem" $ do
|
||||||
el @ att "class" nbFontHeading1Class $ text "Account Created!"
|
el @ att "class" nbHeadingClass $ text "Account Created!"
|
||||||
el @ att "style" "margin-top:1rem;margin-bottom:1rem" $ text "Your account has been created."
|
el @ att "style" "margin-top:1rem;margin-bottom:1rem" $ text "Your account has been created."
|
||||||
route RDashboard $ text "Go to Dashboard"
|
route RDashboard @ att "class" nbButtonDefaultClass $ text "Go to Dashboard"
|
||||||
|
|
||||||
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
|
||||||
el @ att "class" nbBoxClass @ att "style" "padding:2rem" $ do
|
el @ att "class" nbBoxClass @ att "style" "padding:2rem" $ do
|
||||||
el @ att "class" nbFontHeading1Class $ text "Create Account"
|
el @ att "class" nbHeadingClass $ text "Create Account"
|
||||||
el @ att "style" "opacity:0.7;margin-bottom:1.5rem" $ text "Join your household chore tracker."
|
el @ att "style" "opacity:0.7;margin-bottom:1.5rem" $ text "Join your household chore tracker."
|
||||||
case mError of
|
case mError of
|
||||||
Just err ->
|
Just err ->
|
||||||
@@ -79,10 +79,10 @@ signupView mError = do
|
|||||||
el @ att "class" nbLabelClass $ text "Confirm Password"
|
el @ att "class" nbLabelClass $ text "Confirm Password"
|
||||||
tag "input" @ att "type" "password" . att "name" "sfConfirm" . att "class" nbInputClass @ att "style" "width:100%" $ none
|
tag "input" @ att "type" "password" . att "name" "sfConfirm" . att "class" nbInputClass @ att "style" "width:100%" $ none
|
||||||
el @ att "style" "display:flex;align-items:center;gap:0.5rem;margin-bottom:1rem" $ do
|
el @ att "style" "display:flex;align-items:center;gap:0.5rem;margin-bottom:1rem" $ do
|
||||||
tag "input" @ att "type" "checkbox" . att "name" "sfAgree" $ none
|
tag "input" @ att "type" "checkbox" . att "name" "sfAgree" . att "class" "nb-checkbox" $ none
|
||||||
text "I agree to the terms of service"
|
el @ att "class" nbLabelClass $ 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" nbButtonDefaultClass @ att "style" "width:100%"
|
||||||
route RLogin $ text "Already have an account? Log In"
|
route RLogin @ att "class" nbButtonDefaultClass $ text "Log In"
|
||||||
page :: (Hyperbole :> es, DB :> es, IOE :> es) => Page es '[SignupPage]
|
page :: (Hyperbole :> es, DB :> es, IOE :> es) => Page es '[SignupPage]
|
||||||
page = do
|
page = do
|
||||||
mSession <- lookupSession @UserSession
|
mSession <- lookupSession @UserSession
|
||||||
|
|||||||
+32
-26
@@ -1,42 +1,48 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
{- | Neo Brutalism CSS class name constants for Hyperbole views.
|
-- | Neo Brutalism CSS class name constants for Hyperbole views.
|
||||||
Use with: \@ att \"class\" nbBoxClass
|
-- Based on https://github.com/matifandy8/NeoBrutalismCSS
|
||||||
-}
|
module Sis.Style
|
||||||
module Sis.Style (
|
( nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass
|
||||||
nbBoxClass,
|
, nbBadgeClass, nbHeadingClass
|
||||||
nbButtonClass,
|
, nbNavbarClass, nbNavbarLinkClass, nbContainerClass, nbListItemClass
|
||||||
nbInputClass,
|
, colorRed, colorYellow, colorGreen
|
||||||
nbLabelClass,
|
) where
|
||||||
nbBadgeClass,
|
|
||||||
nbFontHeading1Class,
|
|
||||||
nbFontHeading2Class,
|
|
||||||
nbNavbarClass,
|
|
||||||
nbContainerClass,
|
|
||||||
nbListItemClass,
|
|
||||||
colorRed,
|
|
||||||
colorYellow,
|
|
||||||
colorGreen,
|
|
||||||
) where
|
|
||||||
|
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
|
|
||||||
nbBoxClass, nbButtonClass, nbInputClass, nbLabelClass, nbBadgeClass :: Text
|
-- Box/panel
|
||||||
nbBoxClass = "nb-box"
|
nbBoxClass :: Text
|
||||||
nbButtonClass = "nb-button"
|
nbBoxClass = "nb-card"
|
||||||
|
|
||||||
|
-- Button (default variant: black border, white bg)
|
||||||
|
nbButtonDefaultClass :: Text
|
||||||
|
nbButtonDefaultClass = "nb-button default"
|
||||||
|
|
||||||
|
-- Form elements
|
||||||
|
nbInputClass, nbLabelClass :: Text
|
||||||
nbInputClass = "nb-input"
|
nbInputClass = "nb-input"
|
||||||
nbLabelClass = "nb-label"
|
nbLabelClass = "nb-label"
|
||||||
nbBadgeClass = "nb-badge"
|
|
||||||
|
|
||||||
nbFontHeading1Class, nbFontHeading2Class :: Text
|
-- Badge/pill (inline label)
|
||||||
nbFontHeading1Class = "nb-font-heading1"
|
nbBadgeClass :: Text
|
||||||
nbFontHeading2Class = "nb-font-heading2"
|
nbBadgeClass = "nb-button default"
|
||||||
|
|
||||||
nbNavbarClass, nbContainerClass, nbListItemClass :: Text
|
-- Heading
|
||||||
|
nbHeadingClass :: Text
|
||||||
|
nbHeadingClass = "nb-card-title"
|
||||||
|
|
||||||
|
-- Navbar
|
||||||
|
nbNavbarClass, nbNavbarLinkClass :: Text
|
||||||
nbNavbarClass = "nb-navbar"
|
nbNavbarClass = "nb-navbar"
|
||||||
|
nbNavbarLinkClass = "nb-navbar-link"
|
||||||
|
|
||||||
|
-- Layout
|
||||||
|
nbContainerClass, nbListItemClass :: Text
|
||||||
nbContainerClass = "nb-container"
|
nbContainerClass = "nb-container"
|
||||||
nbListItemClass = "nb-list-item"
|
nbListItemClass = "nb-list-item"
|
||||||
|
|
||||||
|
-- CSS color variables
|
||||||
colorRed, colorYellow, colorGreen :: Text
|
colorRed, colorYellow, colorGreen :: Text
|
||||||
colorRed = "var(--nb-red)"
|
colorRed = "var(--nb-red)"
|
||||||
colorYellow = "var(--nb-yellow)"
|
colorYellow = "var(--nb-yellow)"
|
||||||
|
|||||||
@@ -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
|
import Sis.Style (nbNavbarClass, nbNavbarLinkClass, nbHeadingClass, nbButtonDefaultClass)
|
||||||
import Web.Hyperbole
|
import Web.Hyperbole
|
||||||
import Web.Hyperbole.Effect.Session
|
import Web.Hyperbole.Effect.Session
|
||||||
|
|
||||||
@@ -44,8 +44,12 @@ documentHead = do
|
|||||||
title "Sis — Household Chore Tracker"
|
title "Sis — Household Chore Tracker"
|
||||||
mobileFriendly
|
mobileFriendly
|
||||||
meta @ att "charset" "UTF-8"
|
meta @ att "charset" "UTF-8"
|
||||||
stylesheet "https://unpkg.com/neobrutalismcss@latest"
|
stylesheet "https://cdn.jsdelivr.net/gh/matifandy8/NeoBrutalismCSS/dist/index.min.css"
|
||||||
stylesheet "/static/style.css"
|
stylesheet "/static/style.css"
|
||||||
|
-- Lexend Mega font for headings/buttons (used by NeoBrutalismCSS)
|
||||||
|
tag "link" @ att "rel" "preconnect" . att "href" "https://fonts.googleapis.com" $ none
|
||||||
|
tag "link" @ att "rel" "preconnect" . att "href" "https://fonts.gstatic.com" @ att "crossorigin" "" $ none
|
||||||
|
tag "link" @ att "href" "https://fonts.googleapis.com/css2?family=Lexend+Mega:wght@900&family=Public+Sans:wght@400;600;700&display=swap" . att "rel" "stylesheet" $ none
|
||||||
tag "link" @ att "rel" "manifest" . att "href" "/static/manifest.json" $ none
|
tag "link" @ att "rel" "manifest" . att "href" "/static/manifest.json" $ none
|
||||||
script' scriptEmbed
|
script' scriptEmbed
|
||||||
|
|
||||||
@@ -56,9 +60,8 @@ documentHead = do
|
|||||||
navbar :: View ctx ()
|
navbar :: View ctx ()
|
||||||
navbar = do
|
navbar = do
|
||||||
el @ att "class" nbNavbarClass $ do
|
el @ att "class" nbNavbarClass $ do
|
||||||
el @ att "class" "nb-navbar-start" $ do
|
el @ att "class" "nb-navbar-brand" @ att "style" "font-weight:700" $ text "Sis"
|
||||||
el @ att "class" nbFontHeading2Class @ att "style" "font-weight:700" $ text "Sis"
|
el @ att "class" "nb-navbar-nav" $ do
|
||||||
el @ att "class" "nb-navbar-end" $ do
|
|
||||||
routeLink RDashboard "Dashboard"
|
routeLink RDashboard "Dashboard"
|
||||||
routeLink RChores "Chores"
|
routeLink RChores "Chores"
|
||||||
routeLink RHousehold "Household"
|
routeLink RHousehold "Household"
|
||||||
@@ -67,4 +70,4 @@ navbar = do
|
|||||||
|
|
||||||
routeLink :: (Route r) => r -> Text -> View ctx ()
|
routeLink :: (Route r) => r -> Text -> View ctx ()
|
||||||
routeLink rt lbl = do
|
routeLink rt lbl = do
|
||||||
el @ att "class" nbButtonClass $ route rt $ text lbl
|
el @ att "class" nbNavbarLinkClass $ route rt $ text lbl
|
||||||
|
|||||||
Reference in New Issue
Block a user