module JobsJobsJobs.Views.Layout open Giraffe.ViewEngine open Giraffe.ViewEngine.Accessibility open Giraffe.ViewEngine.Htmx /// Data items needed to render a view type PageRenderContext = { /// Whether a user is logged on IsLoggedOn : bool /// The current URL CurrentUrl : string /// The title of this page PageTitle : string /// The page content Content : XmlNode } /// Append the application name to the page title let private constructTitle ctx = seq { if ctx.PageTitle <> "" then ctx.PageTitle; " | " "Jobs, Jobs, Jobs" } |> Seq.reduce (+) |> str |> List.singleton |> title [] /// Generate the HTML head tag let private htmlHead ctx = head [] [ meta [ _name "viewport"; _content "width=device-width, initial-scale=1" ] constructTitle ctx link [ _href "https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" _rel "stylesheet" _integrity "sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" _crossorigin "anonymous" ] link [ _href "https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css" _rel "stylesheet" ] link [ _href "/style.css"; _rel "stylesheet" ] ] /// Display the links available to the current user let private links ctx = let navLink url icon text = a [ _href url _onclick "jjj.hideMenu()" if url = ctx.CurrentUrl then _class "jjj-current-page" ] [ i [ _class $"mdi mdi-{icon}"; _ariaHidden "true" ] []; rawText text ] nav [ _class "jjj-nav" ] [ if ctx.IsLoggedOn then navLink "/citizen/dashboard" "view-dashboard-variant" "Dashboard" navLink "/help-wanted" "newspaper-variant-multiple-outline" "Help Wanted!" navLink "/profile/search" "view-list-outline" "Employment Profiles" navLink "/success-story/list" "thumb-up" "Success Stories" div [ _class "separator" ] [] navLink "/citizen/account" "account-edit" "My Account" navLink "/listings/mine" "sign-text" "My Job Listings" navLink "/profile/edit" "pencil" "My Employment Profile" div [ _class "separator" ] [] navLink "/citizen/log-off" "mdiLogoutVariant" "Log Off" else navLink "/" "home" "Home" navLink "/profile/seeking" "view-list-outline" "Job Seekers" navLink "/citizen/log-on" "login-variant" "Log On" navLink "/how-it-works" "help-circle-outline" "How It Works" ] /// Generate mobile and desktop side navigation areas let private sideNavs ctx = [ div [ _id "mobileMenu" _class "jjj-mobile-menu offcanvas offcanvas-end" _tabindex "-1" _ariaLabelledBy "mobileMenuLabel" ] [ div [ _class "offcanvas-header" ] [ h5 [ _id "mobileMenuLabel" ] [ rawText "Menu" ] button [ _class "btn-close text-reset"; _type "button"; _data "bs-dismiss" "offcanvas"; _ariaLabel "Close" ] [] ] div [ _class "offcanvas-body" ] [ links ctx ] ] aside [ _class "jjj-full-menu d-none d-md-block p-3" ] [ p [ _class "home-link pb-3" ] [ a [ _href "/" ] [ rawText "Jobs, Jobs, Jobs" ] ] p [] [ rawText " " ] links ctx ] ] /// Title bars for mobile and desktop let private titleBars = [ nav [ _class "d-flex d-md-none navbar navbar-dark" ] [ span [ _class "navbar-text" ] [ a [ _href "/" ] [ rawText "Jobs, Jobs, Jobs" ] ] button [ _class "btn" _data "bs-toggle" "offcanvas" _data "bs-target" "#mobileMenu" _ariaControls "mobileMenu" ] [ i [ _class "mdi mdi-menu" ] [] ] ] nav [ _class "d-none d-md-flex navbar navbar-light bg-light"] [ span [] [ rawText " " ] span [ _class "navbar-text" ] [ rawText "(…and Jobs – " audioClip "pelosi-jobs" (rawText "Let’s Vote for Jobs!") rawText ")" ] ] ] /// The HTML footer for the page let private htmlFoot = let v = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version let version = seq { string v.Major if v.Minor > 0 then "."; string v.Minor if v.Build > 0 then "."; string v.Build } |> Seq.reduce (+) footer [] [ p [ _class "text-muted" ] [ str "Jobs, Jobs, Jobs v"; str version; rawText " • " a [ _href "/privacy-policy" ] [ str "Privacy Policy" ]; rawText " • " a [ _href "/terms-of-service" ] [ str "Terms of Service" ] ] ] /// Create a full view let full ctx = html [ _lang "en" ] [ htmlHead ctx body [] [ div [ _class "jjj-app"; _hxBoost; _hxTarget "this" ] [ yield! sideNavs ctx div [ _class "jjj-main" ] [ yield! titleBars main [ _class "jjj-content container-fluid" ] [ ctx.Content ] htmlFoot ] ] Script.minified script [ _async _src "https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" _integrity "sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" _crossorigin "anonymous" ] [] script [ _src "/script.js" ] [] ] ] /// Create a partial (boosted response) view let partial ctx = html [ _lang "en" ] [ head [] [ constructTitle ctx ] body [] [ yield! sideNavs ctx div [ _class "jjj-main" ] [ yield! titleBars main [ _class "jjj-content container-fluid" ] [ ctx.Content ] htmlFoot ] ] ]