Add style/onload to render context (#36)

This commit is contained in:
Daniel J. Summers 2022-07-31 21:25:44 -04:00
parent 1547377527
commit f66dd76dfb
9 changed files with 683 additions and 611 deletions

View File

@ -5,57 +5,62 @@ open PrayerTracker.Entities
open PrayerTracker.ViewModels open PrayerTracker.ViewModels
/// View for the church edit page /// View for the church edit page
let edit (m : EditChurch) ctx vi = let edit (model : EditChurch) ctx viewInfo =
let pageTitle = if m.IsNew then "Add a New Church" else "Edit Church" let pageTitle = if model.IsNew then "Add a New Church" else "Edit Church"
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
[ form [ _action "/church/save"; _method "post"; _class "pt-center-columns"; Target.content ] [ let vi =
style [ _scoped ] [ viewInfo
rawText "#name { width: 20rem; } #city { width: 10rem; } #st { width: 3rem; } #interfaceAddress { width: 30rem; }" |> AppViewInfo.withScopedStyles [
"#name { width: 20rem; }"
"#city { width: 10rem; }"
"#st { width: 3rem; }"
"#interfaceAddress { width: 30rem; }"
] ]
|> AppViewInfo.withOnLoadScript "PT.church.edit.onPageLoad"
form [ _action "/church/save"; _method "post"; _class "pt-center-columns"; Target.content ] [
csrfToken ctx csrfToken ctx
input [ _type "hidden"; _name (nameof m.ChurchId); _value (flatGuid m.ChurchId) ] input [ _type "hidden"; _name (nameof model.ChurchId); _value (flatGuid model.ChurchId) ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "name" ] [ locStr s["Church Name"] ] label [ _for "name" ] [ locStr s["Church Name"] ]
input [ _type "text"; _name (nameof m.Name); _id "name"; _required; _autofocus; _value m.Name ] input [ _type "text"; _name (nameof model.Name); _id "name"; _required; _autofocus; _value model.Name ]
] ]
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "City"] [ locStr s["City"] ] label [ _for "City"] [ locStr s["City"] ]
input [ _type "text"; _name (nameof m.City); _id "city"; _required; _value m.City ] input [ _type "text"; _name (nameof model.City); _id "city"; _required; _value model.City ]
] ]
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "state" ] [ locStr s["State or Province"] ] label [ _for "state" ] [ locStr s["State or Province"] ]
input [ _type "text" input [ _type "text"
_name (nameof m.State) _name (nameof model.State)
_id "state" _id "state"
_minlength "2"; _maxlength "2" _minlength "2"; _maxlength "2"
_value m.State _value model.State
_required ] _required ]
] ]
] ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-checkbox-field" ] [ div [ _checkboxField ] [
input [ _type "checkbox" input [ _type "checkbox"
_name (nameof m.HasInterface) _name (nameof model.HasInterface)
_id "hasInterface" _id "hasInterface"
_value "True" _value "True"
if defaultArg m.HasInterface false then _checked ] if defaultArg model.HasInterface false then _checked ]
label [ _for "hasInterface" ] [ locStr s["Has an interface with Virtual Prayer Room"] ] label [ _for "hasInterface" ] [ locStr s["Has an interface with Virtual Prayer Room"] ]
] ]
] ]
div [ _class "pt-field-row pt-fadeable"; _id "divInterfaceAddress" ] [ div [ _fieldRowWith [ "pt-fadeable" ]; _id "divInterfaceAddress" ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "interfaceAddress" ] [ locStr s["VPR Interface URL"] ] label [ _for "interfaceAddress" ] [ locStr s["VPR Interface URL"] ]
input [ _type "url" input [ _type "url"
_name (nameof m.InterfaceAddress) _name (nameof model.InterfaceAddress)
_id "interfaceAddress"; _id "interfaceAddress";
_value (defaultArg m.InterfaceAddress "") ] _value (defaultArg model.InterfaceAddress "") ]
] ]
] ]
div [ _class "pt-field-row" ] [ submit [] "save" s["Save Church"] ] div [ _fieldRow ] [ submit [] "save" s["Save Church"] ]
]
script [] [ rawText "PT.onLoad(PT.church.edit.onPageLoad)" ]
] ]
|> List.singleton
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi pageTitle |> Layout.standard vi pageTitle

View File

@ -119,6 +119,25 @@ let _onsubmit = attr "onsubmit"
/// A "rel='noopener'" attribute /// A "rel='noopener'" attribute
let _relNoOpener = _rel "noopener" let _relNoOpener = _rel "noopener"
/// A class attribute that designates a row of fields, with the additional classes passed
let _fieldRowWith classes =
let extraClasses = if List.isEmpty classes then "" else $""" {classes |> String.concat " "}"""
_class $"pt-field-row{extraClasses}"
/// The class that designates a row of fields
let _fieldRow = _fieldRowWith []
/// A class attribute that designates an input field, with the additional classes passed
let _inputFieldWith classes =
let extraClasses = if List.isEmpty classes then "" else $""" {classes |> String.concat " "}"""
_class $"pt-field{extraClasses}"
/// The class that designates an input field / label pair
let _inputField = _inputFieldWith []
/// The class that designates a checkbox / label pair
let _checkboxField = _class "pt-checkbox-field"
/// The name this function used to have when the view engine was part of Giraffe /// The name this function used to have when the view engine was part of Giraffe
let renderHtmlNode = RenderView.AsString.htmlNode let renderHtmlNode = RenderView.AsString.htmlNode

View File

@ -6,7 +6,7 @@ open Giraffe.ViewEngine
open PrayerTracker.ViewModels open PrayerTracker.ViewModels
/// The error page /// The error page
let error code vi = let error code viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let l = I18N.forView "Home/Error" let l = I18N.forView "Home/Error"
use sw = new StringWriter () use sw = new StringWriter ()
@ -37,7 +37,7 @@ let error code vi =
_alt $"""%A{s["PrayerTracker"]} %A{s["from Bit Badger Solutions"]}""" _alt $"""%A{s["PrayerTracker"]} %A{s["from Bit Badger Solutions"]}"""
_title $"""%A{s["PrayerTracker"]} %A{s["from Bit Badger Solutions"]}""" _title $"""%A{s["PrayerTracker"]} %A{s["from Bit Badger Solutions"]}"""
_style "vertical-align:text-bottom;" ] _style "vertical-align:text-bottom;" ]
str vi.Version str viewInfo.Version
] ]
] ]
|> div [] |> div []
@ -45,7 +45,7 @@ let error code vi =
/// The home page /// The home page
let index vi = let index viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let l = I18N.forView "Home/Index" let l = I18N.forView "Home/Index"
use sw = new StringWriter () use sw = new StringWriter ()
@ -118,11 +118,11 @@ let index vi =
] ]
] ]
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi "Welcome!" |> Layout.standard viewInfo "Welcome!"
/// Privacy Policy page /// Privacy Policy page
let privacyPolicy vi = let privacyPolicy viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let l = I18N.forView "Home/PrivacyPolicy" let l = I18N.forView "Home/PrivacyPolicy"
use sw = new StringWriter () use sw = new StringWriter ()
@ -190,11 +190,11 @@ let privacyPolicy vi =
] ]
] ]
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi "Privacy Policy" |> Layout.standard viewInfo "Privacy Policy"
/// Terms of Service page /// Terms of Service page
let termsOfService vi = let termsOfService viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let l = I18N.forView "Home/TermsOfService" let l = I18N.forView "Home/TermsOfService"
use sw = new StringWriter () use sw = new StringWriter ()
@ -237,11 +237,11 @@ let termsOfService vi =
p [] [ raw l["You may also wish to review our {0} to learn how we handle your data.", ppLink] ] p [] [ raw l["You may also wish to review our {0} to learn how we handle your data.", ppLink] ]
] ]
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi "Terms of Service" |> Layout.standard viewInfo "Terms of Service"
/// View for unauthorized page /// View for unauthorized page
let unauthorized vi = let unauthorized viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let l = I18N.forView "Home/Unauthorized" let l = I18N.forView "Home/Unauthorized"
use sw = new StringWriter () use sw = new StringWriter ()
@ -256,4 +256,4 @@ let unauthorized vi =
] ]
] ]
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi "Unauthorized Access" |> Layout.standard viewInfo "Unauthorized Access"

View File

@ -293,10 +293,16 @@ let private contentSection viewInfo title (content : XmlNode) = [
Navigation.identity viewInfo Navigation.identity viewInfo
renderPageTitle viewInfo title renderPageTitle viewInfo title
yield! messages viewInfo yield! messages viewInfo
match viewInfo.ScopedStyle with
| [] -> ()
| styles -> style [ _scoped ] (styles |> List.map (fun it -> rawText $"{it};"))
content content
htmlFooter viewInfo htmlFooter viewInfo
for jsFile in viewInfo.Script do for jsFile in viewInfo.Script do
script [ _src $"/js/{jsFile}.js" ] [] script [ _src $"/js/{jsFile}.js" ] []
match viewInfo.OnLoadScript with
| Some onLoad -> script [] [ rawText $"PT.onLoad({onLoad}" ]
| None -> ()
] ]
/// The HTML head element for partial responses /// The HTML head element for partial responses

View File

@ -11,38 +11,40 @@ open PrayerTracker.Entities
open PrayerTracker.ViewModels open PrayerTracker.ViewModels
/// View for the prayer request edit page /// View for the prayer request edit page
let edit (m : EditRequest) today ctx vi = let edit (model : EditRequest) today ctx viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let pageTitle = if m.IsNew then "Add a New Request" else "Edit Request" let pageTitle = if model.IsNew then "Add a New Request" else "Edit Request"
[ form [ _action "/prayer-request/save"; _method "post"; _class "pt-center-columns"; Target.content ] [ let vi = AppViewInfo.withOnLoadScript "PT.initCKEditor" viewInfo
form [ _action "/prayer-request/save"; _method "post"; _class "pt-center-columns"; Target.content ] [
csrfToken ctx csrfToken ctx
input [ _type "hidden"; _name (nameof m.RequestId); _value (flatGuid m.RequestId) ] input [ _type "hidden"; _name (nameof model.RequestId); _value (flatGuid model.RequestId) ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for (nameof m.RequestType) ] [ locStr s["Request Type"] ] label [ _for (nameof model.RequestType) ] [ locStr s["Request Type"] ]
ReferenceList.requestTypeList s ReferenceList.requestTypeList s
|> Seq.ofList |> Seq.ofList
|> Seq.map (fun (typ, desc) -> typ.code, desc.Value) |> Seq.map (fun (typ, desc) -> typ.code, desc.Value)
|> selectList (nameof m.RequestType) m.RequestType [ _required; _autofocus ] |> selectList (nameof model.RequestType) model.RequestType [ _required; _autofocus ]
] ]
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "requestor" ] [ locStr s["Requestor / Subject"] ] label [ _for "requestor" ] [ locStr s["Requestor / Subject"] ]
input [ _type "text" input [ _type "text"
_id "requestor" _id "requestor"
_name (nameof m.Requestor) _name (nameof model.Requestor)
_value (defaultArg m.Requestor "") ] _value (defaultArg model.Requestor "") ]
] ]
if m.IsNew then if model.IsNew then
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "enteredDate" ] [ locStr s["Date"] ] label [ _for "enteredDate" ] [ locStr s["Date"] ]
input [ _type "date"; _name (nameof m.EnteredDate); _id "enteredDate"; _placeholder today ] input [ _type "date"; _name (nameof model.EnteredDate); _id "enteredDate"; _placeholder today ]
] ]
else else
div [ _class "pt-field" ] [ // TODO: do these need to be nested like this?
div [ _class "pt-checkbox-field" ] [ div [ _inputField ] [
div [ _checkboxField ] [
br [] br []
input [ _type "checkbox" input [ _type "checkbox"
_name (nameof m.SkipDateUpdate) _name (nameof model.SkipDateUpdate)
_id "skipDateUpdate" _id "skipDateUpdate"
_value "True" ] _value "True" ]
label [ _for "skipDateUpdate" ] [ locStr s["Check to not update the date"] ] label [ _for "skipDateUpdate" ] [ locStr s["Check to not update the date"] ]
@ -51,39 +53,38 @@ let edit (m : EditRequest) today ctx vi =
] ]
] ]
] ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [] [ locStr s["Expiration"] ] label [] [ locStr s["Expiration"] ]
ReferenceList.expirationList s (not m.IsNew) ReferenceList.expirationList s (not model.IsNew)
|> List.map (fun exp -> |> List.map (fun exp ->
let radioId = $"expiration_{fst exp}" let radioId = $"expiration_{fst exp}"
span [ _class "text-nowrap" ] [ span [ _class "text-nowrap" ] [
radio (nameof m.Expiration) radioId (fst exp) m.Expiration radio (nameof model.Expiration) radioId (fst exp) model.Expiration
label [ _for radioId ] [ locStr (snd exp) ] label [ _for radioId ] [ locStr (snd exp) ]
rawText "     " rawText "     "
]) ])
|> div [ _class "pt-center-text" ] |> div [ _class "pt-center-text" ]
] ]
] ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field pt-editor" ] [ div [ _inputFieldWith [ "pt-editor" ] ] [
label [ _for "text" ] [ locStr s["Request"] ] label [ _for "text" ] [ locStr s["Request"] ]
textarea [ _name (nameof m.Text); _id "text" ] [ str m.Text ] textarea [ _name (nameof model.Text); _id "text" ] [ str model.Text ]
] ]
] ]
div [ _class "pt-field-row" ] [ submit [] "save" s["Save Request"] ] div [ _fieldRow ] [ submit [] "save" s["Save Request"] ]
]
script [] [ rawText "PT.onLoad(PT.initCKEditor)" ]
] ]
|> List.singleton
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi pageTitle |> Layout.standard vi pageTitle
/// View for the request e-mail results page /// View for the request e-mail results page
let email m vi = let email model viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let pageTitle = $"""{s["Prayer Requests"].Value} {m.SmallGroup.name}""" let pageTitle = $"""{s["Prayer Requests"].Value} {model.SmallGroup.name}"""
let prefs = m.SmallGroup.preferences let prefs = model.SmallGroup.preferences
let addresses = m.Recipients |> List.map (fun mbr -> $"{mbr.memberName} <{mbr.email}>") |> String.concat ", " let addresses = model.Recipients |> List.map (fun mbr -> $"{mbr.memberName} <{mbr.email}>") |> String.concat ", "
[ p [ _style $"font-family:{prefs.listFonts};font-size:%i{prefs.textFontSize}pt;" ] [ [ p [ _style $"font-family:{prefs.listFonts};font-size:%i{prefs.textFontSize}pt;" ] [
locStr s["The request list was sent to the following people, via individual e-mails"] locStr s["The request list was sent to the following people, via individual e-mails"]
rawText ":" rawText ":"
@ -91,27 +92,27 @@ let email m vi =
small [] [ str addresses ] small [] [ str addresses ]
] ]
span [ _class "pt-email-heading" ] [ locStr s["HTML Format"]; rawText ":" ] span [ _class "pt-email-heading" ] [ locStr s["HTML Format"]; rawText ":" ]
div [ _class "pt-email-canvas" ] [ rawText (m.AsHtml s) ] div [ _class "pt-email-canvas" ] [ rawText (model.AsHtml s) ]
br [] br []
br [] br []
span [ _class "pt-email-heading" ] [ locStr s["Plain-Text Format"]; rawText ":" ] span [ _class "pt-email-heading" ] [ locStr s["Plain-Text Format"]; rawText ":" ]
div [ _class "pt-email-canvas" ] [ pre [] [ str (m.AsText s) ] ] div [ _class "pt-email-canvas" ] [ pre [] [ str (model.AsText s) ] ]
] ]
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi pageTitle |> Layout.standard viewInfo pageTitle
/// View for a small group's public prayer request list /// View for a small group's public prayer request list
let list (m : RequestList) vi = let list (model : RequestList) viewInfo =
[ br [] [ br []
I18N.localizer.Force () |> (m.AsHtml >> rawText) I18N.localizer.Force () |> (model.AsHtml >> rawText)
] ]
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi "View Request List" |> Layout.standard viewInfo "View Request List"
/// View for the prayer request lists page /// View for the prayer request lists page
let lists (groups : SmallGroup list) vi = let lists (groups : SmallGroup list) viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let l = I18N.forView "Requests/Lists" let l = I18N.forView "Requests/Lists"
use sw = new StringWriter () use sw = new StringWriter ()
@ -154,17 +155,17 @@ let lists (groups : SmallGroup list) vi =
] ]
] ]
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi "Request Lists" |> Layout.standard viewInfo "Request Lists"
/// View for the prayer request maintenance page /// View for the prayer request maintenance page
let maintain (m : MaintainRequests) (ctx : HttpContext) vi = let maintain (model : MaintainRequests) (ctx : HttpContext) viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let l = I18N.forView "Requests/Maintain" let l = I18N.forView "Requests/Maintain"
use sw = new StringWriter () use sw = new StringWriter ()
let raw = rawLocText sw let raw = rawLocText sw
let now = m.SmallGroup.localDateNow (ctx.GetService<IClock> ()) let now = model.SmallGroup.localDateNow (ctx.GetService<IClock> ())
let prefs = m.SmallGroup.preferences let prefs = model.SmallGroup.preferences
let types = ReferenceList.requestTypeList s |> Map.ofList let types = ReferenceList.requestTypeList s |> Map.ofList
let updReq (req : PrayerRequest) = let updReq (req : PrayerRequest) =
if req.updateRequired now prefs.daysToExpire prefs.longTermUpdateWeeks then "pt-request-update" else "" if req.updateRequired now prefs.daysToExpire prefs.longTermUpdateWeeks then "pt-request-update" else ""
@ -173,7 +174,7 @@ let maintain (m : MaintainRequests) (ctx : HttpContext) vi =
_class (if req.isExpired now prefs.daysToExpire then "pt-request-expired" else "") _class (if req.isExpired now prefs.daysToExpire then "pt-request-expired" else "")
/// Iterate the sequence once, before we render, so we can get the count of it at the top of the table /// Iterate the sequence once, before we render, so we can get the count of it at the top of the table
let requests = let requests =
m.Requests model.Requests
|> List.map (fun req -> |> List.map (fun req ->
let reqId = flatGuid req.prayerRequestId let reqId = flatGuid req.prayerRequestId
let reqText = htmlToPlainText req.text let reqText = htmlToPlainText req.text
@ -228,7 +229,7 @@ let maintain (m : MaintainRequests) (ctx : HttpContext) vi =
a [ _href "/prayer-requests/view"; _title s["View Prayer Request List"].Value ] [ a [ _href "/prayer-requests/view"; _title s["View Prayer Request List"].Value ] [
icon "list"; rawText " &nbsp;"; locStr s["View Prayer Request List"] icon "list"; rawText " &nbsp;"; locStr s["View Prayer Request List"]
] ]
match m.SearchTerm with match model.SearchTerm with
| Some _ -> | Some _ ->
rawText " &nbsp; &nbsp; &nbsp; " rawText " &nbsp; &nbsp; &nbsp; "
a [ _href "/prayer-requests"; _title l["Clear Search Criteria"].Value ] [ a [ _href "/prayer-requests"; _title l["Clear Search Criteria"].Value ] [
@ -240,7 +241,7 @@ let maintain (m : MaintainRequests) (ctx : HttpContext) vi =
input [ _type "text" input [ _type "text"
_name "search" _name "search"
_placeholder l["Search requests..."].Value _placeholder l["Search requests..."].Value
_value (defaultArg m.SearchTerm "") _value (defaultArg model.SearchTerm "")
] ]
space space
submit [] "search" s["Search"] submit [] "search" s["Search"]
@ -264,22 +265,22 @@ let maintain (m : MaintainRequests) (ctx : HttpContext) vi =
] ]
div [ _class "pt-center-text" ] [ div [ _class "pt-center-text" ] [
br [] br []
match m.OnlyActive with match model.OnlyActive with
| Some true -> | Some true ->
raw l["Inactive requests are currently not shown"] raw l["Inactive requests are currently not shown"]
br [] br []
a [ _href "/prayer-requests/inactive" ] [ raw l["Show Inactive Requests"] ] a [ _href "/prayer-requests/inactive" ] [ raw l["Show Inactive Requests"] ]
| _ -> | _ ->
if defaultArg m.OnlyActive false then if defaultArg model.OnlyActive false then
raw l["Inactive requests are currently shown"] raw l["Inactive requests are currently shown"]
br [] br []
a [ _href "/prayer-requests" ] [ raw l["Do Not Show Inactive Requests"] ] a [ _href "/prayer-requests" ] [ raw l["Do Not Show Inactive Requests"] ]
br [] br []
br [] br []
let search = [ match m.SearchTerm with Some s -> "search", s | None -> () ] let search = [ match model.SearchTerm with Some s -> "search", s | None -> () ]
let pg = defaultArg m.PageNbr 1 let pg = defaultArg model.PageNbr 1
let url = let url =
match m.OnlyActive with Some true | None -> "" | _ -> "/inactive" match model.OnlyActive with Some true | None -> "" | _ -> "/inactive"
|> sprintf "/prayer-requests%s" |> sprintf "/prayer-requests%s"
match pg with match pg with
| 1 -> () | 1 -> ()
@ -288,7 +289,7 @@ let maintain (m : MaintainRequests) (ctx : HttpContext) vi =
let withPage = match pg with 2 -> search | _ -> ("page", string (pg - 1)) :: search let withPage = match pg with 2 -> search | _ -> ("page", string (pg - 1)) :: search
a [ _href (makeUrl url withPage) ] [ icon "keyboard_arrow_left"; space; raw l["Previous Page"] ] a [ _href (makeUrl url withPage) ] [ icon "keyboard_arrow_left"; space; raw l["Previous Page"] ]
rawText " &nbsp; &nbsp; " rawText " &nbsp; &nbsp; "
match requests.Length = m.SmallGroup.preferences.pageSize with match requests.Length = model.SmallGroup.preferences.pageSize with
| true -> | true ->
a [ _href (makeUrl url (("page", string (pg + 1)) :: search)) ] [ a [ _href (makeUrl url (("page", string (pg + 1)) :: search)) ] [
raw l["Next Page"]; space; icon "keyboard_arrow_right" raw l["Next Page"]; space; icon "keyboard_arrow_right"
@ -298,19 +299,19 @@ let maintain (m : MaintainRequests) (ctx : HttpContext) vi =
form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ] form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ]
] ]
|> Layout.Content.wide |> Layout.Content.wide
|> Layout.standard vi (match m.SearchTerm with Some _ -> "Search Results" | None -> "Maintain Requests") |> Layout.standard viewInfo (match model.SearchTerm with Some _ -> "Search Results" | None -> "Maintain Requests")
/// View for the printable prayer request list /// View for the printable prayer request list
let print m version = let print model version =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let pageTitle = $"""{s["Prayer Requests"].Value} {m.SmallGroup.name}""" let pageTitle = $"""{s["Prayer Requests"].Value} {model.SmallGroup.name}"""
let imgAlt = $"""{s["PrayerTracker"].Value} {s["from Bit Badger Solutions"].Value}""" let imgAlt = $"""{s["PrayerTracker"].Value} {s["from Bit Badger Solutions"].Value}"""
article [] [ article [] [
rawText (m.AsHtml s) rawText (model.AsHtml s)
br [] br []
hr [] hr []
div [ _style $"font-size:70%%;font-family:{m.SmallGroup.preferences.listFonts};" ] [ div [ _style $"font-size:70%%;font-family:{model.SmallGroup.preferences.listFonts};" ] [
img [ _src $"""/img/{s["footer_en"].Value}.png""" img [ _src $"""/img/{s["footer_en"].Value}.png"""
_style "vertical-align:text-bottom;" _style "vertical-align:text-bottom;"
_alt imgAlt _alt imgAlt
@ -323,12 +324,12 @@ let print m version =
/// View for the prayer request list /// View for the prayer request list
let view m vi = let view model viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let pageTitle = $"""{s["Prayer Requests"].Value} {m.SmallGroup.name}""" let pageTitle = $"""{s["Prayer Requests"].Value} {model.SmallGroup.name}"""
let spacer = rawText " &nbsp; &nbsp; &nbsp; " let spacer = rawText " &nbsp; &nbsp; &nbsp; "
let dtString = m.Date.ToString "yyyy-MM-dd" let dtString = model.Date.ToString "yyyy-MM-dd"
[ div [ _class "pt-center-text" ] [ div [ _class "pt-center-text" ] [
br [] br []
a [ _class "pt-icon-link" a [ _class "pt-icon-link"
_href $"/prayer-requests/print/{dtString}" _href $"/prayer-requests/print/{dtString}"
@ -336,12 +337,12 @@ let view m vi =
_title s["View Printable"].Value ] [ _title s["View Printable"].Value ] [
icon "print"; rawText " &nbsp;"; locStr s["View Printable"] icon "print"; rawText " &nbsp;"; locStr s["View Printable"]
] ]
if m.CanEmail then if model.CanEmail then
spacer spacer
if m.Date.DayOfWeek <> DayOfWeek.Sunday then if model.Date.DayOfWeek <> DayOfWeek.Sunday then
let rec findSunday (date : DateTime) = let rec findSunday (date : DateTime) =
if date.DayOfWeek = DayOfWeek.Sunday then date else findSunday (date.AddDays 1.) if date.DayOfWeek = DayOfWeek.Sunday then date else findSunday (date.AddDays 1.)
let sunday = findSunday m.Date let sunday = findSunday model.Date
a [ _class "pt-icon-link" a [ _class "pt-icon-link"
_href $"""/prayer-requests/view/{sunday.ToString "yyyy-MM-dd"}""" _href $"""/prayer-requests/view/{sunday.ToString "yyyy-MM-dd"}"""
_title s["List for Next Sunday"].Value ] [ _title s["List for Next Sunday"].Value ] [
@ -360,8 +361,7 @@ let view m vi =
icon "compare_arrows"; rawText " &nbsp;"; locStr s["Maintain Prayer Requests"] icon "compare_arrows"; rawText " &nbsp;"; locStr s["Maintain Prayer Requests"]
] ]
] ]
br [] |> List.singleton
rawText (m.AsHtml s) |> List.append [ br []; rawText (model.AsHtml s) ]
]
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi pageTitle |> Layout.standard viewInfo pageTitle

View File

@ -6,125 +6,125 @@ open PrayerTracker.Entities
open PrayerTracker.ViewModels open PrayerTracker.ViewModels
/// View for the announcement page /// View for the announcement page
let announcement isAdmin ctx vi = let announcement isAdmin ctx viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let m = { SendToClass = ""; Text = ""; AddToRequestList = None; RequestType = None } let model = { SendToClass = ""; Text = ""; AddToRequestList = None; RequestType = None }
let reqTypes = ReferenceList.requestTypeList s let reqTypes = ReferenceList.requestTypeList s
[ form [ _action "/small-group/announcement/send"; _method "post"; _class "pt-center-columns"; Target.content ] [ let vi = AppViewInfo.withOnLoadScript "PT.smallGroup.announcement.onPageLoad" viewInfo
form [ _action "/small-group/announcement/send"; _method "post"; _class "pt-center-columns"; Target.content ] [
csrfToken ctx csrfToken ctx
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field pt-editor" ] [ div [ _inputFieldWith [ "pt-editor" ] ] [
label [ _for "text" ] [ locStr s["Announcement Text"] ] label [ _for "text" ] [ locStr s["Announcement Text"] ]
textarea [ _name (nameof m.Text); _id "text"; _autofocus ] [] textarea [ _name (nameof model.Text); _id "text"; _autofocus ] []
] ]
] ]
if isAdmin then if isAdmin then
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [] [ locStr s["Send Announcement to"]; rawText ":" ] label [] [ locStr s["Send Announcement to"]; rawText ":" ]
div [ _class "pt-center-text" ] [ div [ _class "pt-center-text" ] [
radio (nameof m.SendToClass) "sendY" "Y" "Y" radio (nameof model.SendToClass) "sendY" "Y" "Y"
label [ _for "sendY" ] [ locStr s["This Group"]; rawText " &nbsp; &nbsp; " ] label [ _for "sendY" ] [ locStr s["This Group"]; rawText " &nbsp; &nbsp; " ]
radio (nameof m.SendToClass) "sendN" "N" "Y" radio (nameof model.SendToClass) "sendN" "N" "Y"
label [ _for "sendN" ] [ locStr s["All {0} Users", s["PrayerTracker"]] ] label [ _for "sendN" ] [ locStr s["All {0} Users", s["PrayerTracker"]] ]
] ]
] ]
] ]
else input [ _type "hidden"; _name (nameof m.SendToClass); _value "Y" ] else input [ _type "hidden"; _name (nameof model.SendToClass); _value "Y" ]
div [ _class "pt-field-row pt-fadeable pt-shown"; _id "divAddToList" ] [ div [ _fieldRowWith [ "pt-fadeable"; "pt-shown" ]; _id "divAddToList" ] [
div [ _class "pt-checkbox-field" ] [ div [ _checkboxField ] [
input [ _type "checkbox"; _name (nameof m.AddToRequestList); _id "addToRequestList"; _value "True" ] input [ _type "checkbox"; _name (nameof model.AddToRequestList); _id "addToRequestList"; _value "True" ]
label [ _for "addToRequestList" ] [ locStr s["Add to Request List"] ] label [ _for "addToRequestList" ] [ locStr s["Add to Request List"] ]
] ]
] ]
div [ _class "pt-field-row pt-fadeable"; _id "divCategory" ] [ div [ _fieldRowWith [ "pt-fadeable" ]; _id "divCategory" ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for (nameof m.RequestType) ] [ locStr s["Request Type"] ] label [ _for (nameof model.RequestType) ] [ locStr s["Request Type"] ]
reqTypes reqTypes
|> Seq.ofList |> Seq.ofList
|> Seq.map (fun (typ, desc) -> typ.code, desc.Value) |> Seq.map (fun (typ, desc) -> typ.code, desc.Value)
|> selectList (nameof m.RequestType) Announcement.code [] |> selectList (nameof model.RequestType) Announcement.code []
] ]
] ]
div [ _class "pt-field-row" ] [ submit [] "send" s["Send Announcement"] ] div [ _fieldRow ] [ submit [] "send" s["Send Announcement"] ]
]
script [] [ rawText "PT.onLoad(PT.smallGroup.announcement.onPageLoad)" ]
] ]
|> List.singleton
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi "Send Announcement" |> Layout.standard vi "Send Announcement"
/// View for once an announcement has been sent /// View for once an announcement has been sent
let announcementSent (m : Announcement) vi = let announcementSent (model : Announcement) viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
[ span [ _class "pt-email-heading" ] [ locStr s["HTML Format"]; rawText ":" ] [ span [ _class "pt-email-heading" ] [ locStr s["HTML Format"]; rawText ":" ]
div [ _class "pt-email-canvas" ] [ rawText m.Text ] div [ _class "pt-email-canvas" ] [ rawText model.Text ]
br [] br []
br [] br []
span [ _class "pt-email-heading" ] [ locStr s["Plain-Text Format"]; rawText ":" ] span [ _class "pt-email-heading" ] [ locStr s["Plain-Text Format"]; rawText ":" ]
div [ _class "pt-email-canvas" ] [ pre [] [ str m.PlainText ] ] div [ _class "pt-email-canvas" ] [ pre [] [ str model.PlainText ] ]
] ]
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi "Announcement Sent" |> Layout.standard viewInfo "Announcement Sent"
/// View for the small group add/edit page /// View for the small group add/edit page
let edit (m : EditSmallGroup) (churches : Church list) ctx vi = let edit (model : EditSmallGroup) (churches : Church list) ctx viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let pageTitle = if m.IsNew then "Add a New Group" else "Edit Group" let pageTitle = if model.IsNew then "Add a New Group" else "Edit Group"
form [ _action "/small-group/save"; _method "post"; _class "pt-center-columns"; Target.content ] [ form [ _action "/small-group/save"; _method "post"; _class "pt-center-columns"; Target.content ] [
csrfToken ctx csrfToken ctx
input [ _type "hidden"; _name (nameof m.SmallGroupId); _value (flatGuid m.SmallGroupId) ] input [ _type "hidden"; _name (nameof model.SmallGroupId); _value (flatGuid model.SmallGroupId) ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "name" ] [ locStr s["Group Name"] ] label [ _for "name" ] [ locStr s["Group Name"] ]
input [ _type "text"; _name (nameof m.Name); _id "name"; _value m.Name; _required; _autofocus ] input [ _type "text"; _name (nameof model.Name); _id "name"; _value model.Name; _required; _autofocus ]
] ]
] ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for (nameof m.ChurchId) ] [ locStr s["Church"] ] label [ _for (nameof model.ChurchId) ] [ locStr s["Church"] ]
seq { seq {
"", selectDefault s["Select Church"].Value "", selectDefault s["Select Church"].Value
yield! churches |> List.map (fun c -> flatGuid c.churchId, c.name) yield! churches |> List.map (fun c -> flatGuid c.churchId, c.name)
} }
|> selectList (nameof m.ChurchId) (flatGuid m.ChurchId) [ _required ] |> selectList (nameof model.ChurchId) (flatGuid model.ChurchId) [ _required ]
] ]
] ]
div [ _class "pt-field-row" ] [ submit [] "save" s["Save Group"] ] div [ _fieldRow ] [ submit [] "save" s["Save Group"] ]
] ]
|> List.singleton |> List.singleton
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi pageTitle |> Layout.standard viewInfo pageTitle
/// View for the member edit page /// View for the member edit page
let editMember (m : EditMember) (types : (string * LocalizedString) seq) ctx vi = let editMember (model : EditMember) (types : (string * LocalizedString) seq) ctx viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let pageTitle = if m.IsNew then "Add a New Group Member" else "Edit Group Member" let pageTitle = if model.IsNew then "Add a New Group Member" else "Edit Group Member"
let vi = AppViewInfo.withScopedStyles [ "#name { width: 15rem; }"; "#email { width: 20rem; }" ] viewInfo
form [ _action "/small-group/member/save"; _method "post"; _class "pt-center-columns"; Target.content ] [ form [ _action "/small-group/member/save"; _method "post"; _class "pt-center-columns"; Target.content ] [
style [ _scoped ] [ rawText "#name { width: 15rem; } #email { width: 20rem; }" ]
csrfToken ctx csrfToken ctx
input [ _type "hidden"; _name (nameof m.MemberId); _value (flatGuid m.MemberId) ] input [ _type "hidden"; _name (nameof model.MemberId); _value (flatGuid model.MemberId) ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "name" ] [ locStr s["Member Name"] ] label [ _for "name" ] [ locStr s["Member Name"] ]
input [ _type "text"; _name (nameof m.Name); _id "name"; _required; _autofocus; _value m.Name ] input [ _type "text"; _name (nameof model.Name); _id "name"; _required; _autofocus; _value model.Name ]
] ]
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "email" ] [ locStr s["E-mail Address"] ] label [ _for "email" ] [ locStr s["E-mail Address"] ]
input [ _type "email"; _name (nameof m.Email); _id "email"; _required; _value m.Email ] input [ _type "email"; _name (nameof model.Email); _id "email"; _required; _value model.Email ]
] ]
] ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for (nameof m.Format) ] [ locStr s["E-mail Format"] ] label [ _for (nameof model.Format) ] [ locStr s["E-mail Format"] ]
types types
|> Seq.map (fun typ -> fst typ, (snd typ).Value) |> Seq.map (fun typ -> fst typ, (snd typ).Value)
|> selectList (nameof m.Format) m.Format [] |> selectList (nameof model.Format) model.Format []
] ]
] ]
div [ _class "pt-field-row" ] [ submit [] "save" s["Save"] ] div [ _fieldRow ] [ submit [] "save" s["Save"] ]
] ]
|> List.singleton |> List.singleton
|> Layout.Content.standard |> Layout.Content.standard
@ -132,14 +132,15 @@ let editMember (m : EditMember) (types : (string * LocalizedString) seq) ctx vi
/// View for the small group log on page /// View for the small group log on page
let logOn (groups : SmallGroup list) grpId ctx vi = let logOn (groups : SmallGroup list) grpId ctx viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let m = { SmallGroupId = System.Guid.Empty; Password = ""; RememberMe = None } let model = { SmallGroupId = System.Guid.Empty; Password = ""; RememberMe = None }
[ form [ _action "/small-group/log-on/submit"; _method "post"; _class "pt-center-columns"; Target.body ] [ let vi = AppViewInfo.withOnLoadScript "PT.smallGroup.logOn.onPageLoad" viewInfo
form [ _action "/small-group/log-on/submit"; _method "post"; _class "pt-center-columns"; Target.body ] [
csrfToken ctx csrfToken ctx
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for (nameof m.SmallGroupId) ] [ locStr s["Group"] ] label [ _for (nameof model.SmallGroupId) ] [ locStr s["Group"] ]
seq { seq {
if groups.Length = 0 then "", s["There are no classes with passwords defined"].Value if groups.Length = 0 then "", s["There are no classes with passwords defined"].Value
else else
@ -148,33 +149,32 @@ let logOn (groups : SmallGroup list) grpId ctx vi =
groups groups
|> List.map (fun grp -> flatGuid grp.smallGroupId, $"{grp.church.name} | {grp.name}") |> List.map (fun grp -> flatGuid grp.smallGroupId, $"{grp.church.name} | {grp.name}")
} }
|> selectList (nameof m.SmallGroupId) grpId [ _required ] |> selectList (nameof model.SmallGroupId) grpId [ _required ]
] ]
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "password" ] [ locStr s["Password"] ] label [ _for "password" ] [ locStr s["Password"] ]
input [ _type "password" input [ _type "password"
_name (nameof m.Password) _name (nameof model.Password)
_id "password" _id "password"
_placeholder (s["Case-Sensitive"].Value.ToLower ()) _placeholder (s["Case-Sensitive"].Value.ToLower ())
_required ] _required ]
] ]
] ]
div [ _class "pt-checkbox-field" ] [ div [ _checkboxField ] [
input [ _type "checkbox"; _name (nameof m.RememberMe); _id "rememberMe"; _value "True" ] input [ _type "checkbox"; _name (nameof model.RememberMe); _id "rememberMe"; _value "True" ]
label [ _for "rememberMe" ] [ locStr s["Remember Me"] ] label [ _for "rememberMe" ] [ locStr s["Remember Me"] ]
br [] br []
small [] [ em [] [ str (s["Requires Cookies"].Value.ToLower ()) ] ] small [] [ em [] [ str (s["Requires Cookies"].Value.ToLower ()) ] ]
] ]
div [ _class "pt-field-row" ] [ submit [] "account_circle" s["Log On"] ] div [ _fieldRow ] [ submit [] "account_circle" s["Log On"] ]
]
script [] [ rawText "PT.onLoad(PT.smallGroup.logOn.onPageLoad)" ]
] ]
|> List.singleton
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi "Group Log On" |> Layout.standard vi "Group Log On"
/// View for the small group maintenance page /// View for the small group maintenance page
let maintain (groups : SmallGroup list) ctx vi = let maintain (groups : SmallGroup list) ctx viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let grpTbl = let grpTbl =
match groups with match groups with
@ -223,11 +223,11 @@ let maintain (groups : SmallGroup list) ctx vi =
form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ] form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ]
] ]
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi "Maintain Groups" |> Layout.standard viewInfo "Maintain Groups"
/// View for the member maintenance page /// View for the member maintenance page
let members (members : Member list) (emailTyps : Map<string, LocalizedString>) ctx vi = let members (members : Member list) (emailTypes : Map<string, LocalizedString>) ctx viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let mbrTbl = let mbrTbl =
match members with match members with
@ -262,7 +262,7 @@ let members (members : Member list) (emailTyps : Map<string, LocalizedString>) c
] ]
td [] [ str mbr.memberName ] td [] [ str mbr.memberName ]
td [] [ str mbr.email ] td [] [ str mbr.email ]
td [] [ locStr emailTyps[defaultArg mbr.format ""] ] td [] [ locStr emailTypes[defaultArg mbr.format ""] ]
]) ])
|> tbody [] |> tbody []
] ]
@ -279,13 +279,13 @@ let members (members : Member list) (emailTyps : Map<string, LocalizedString>) c
form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ] form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ]
] ]
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi "Maintain Group Members" |> Layout.standard viewInfo "Maintain Group Members"
open Giraffe.ViewEngine.Accessibility open Giraffe.ViewEngine.Accessibility
/// View for the small group overview page /// View for the small group overview page
let overview m vi = let overview model viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let linkSpacer = rawText "&nbsp; " let linkSpacer = rawText "&nbsp; "
let types = ReferenceList.requestTypeList s |> dict let types = ReferenceList.requestTypeList s |> dict
@ -304,16 +304,16 @@ let overview m vi =
header [ _roleHeading ] [ iconSized 72 "question_answer"; locStr s["Prayer Requests"] ] header [ _roleHeading ] [ iconSized 72 "question_answer"; locStr s["Prayer Requests"] ]
div [] [ div [] [
p [ _class "pt-center-text" ] [ p [ _class "pt-center-text" ] [
strong [] [ str (m.TotalActiveReqs.ToString "N0"); space; locStr s["Active Requests"] ] strong [] [ str (model.TotalActiveReqs.ToString "N0"); space; locStr s["Active Requests"] ]
] ]
hr [] hr []
for cat in m.ActiveReqsByType do for cat in model.ActiveReqsByType do
str (cat.Value.ToString "N0") str (cat.Value.ToString "N0")
space space
locStr types[cat.Key] locStr types[cat.Key]
br [] br []
br [] br []
str (m.AllReqs.ToString "N0") str (model.AllReqs.ToString "N0")
space space
locStr s["Total Requests"] locStr s["Total Requests"]
hr [] hr []
@ -325,7 +325,7 @@ let overview m vi =
section [ _ariaLabel "Small group members" ] [ section [ _ariaLabel "Small group members" ] [
header [ _roleHeading ] [ iconSized 72 "people_outline"; locStr s["Group Members"] ] header [ _roleHeading ] [ iconSized 72 "people_outline"; locStr s["Group Members"] ]
div [ _class "pt-center-text" ] [ div [ _class "pt-center-text" ] [
strong [] [ str (m.TotalMembers.ToString "N0"); space; locStr s["Members"] ] strong [] [ str (model.TotalMembers.ToString "N0"); space; locStr s["Members"] ]
hr [] hr []
a [ _href "/small-group/members" ] [ icon "email"; linkSpacer; locStr s["Maintain Group Members"] ] a [ _href "/small-group/members" ] [ icon "email"; linkSpacer; locStr s["Maintain Group Members"] ]
] ]
@ -333,33 +333,39 @@ let overview m vi =
] ]
|> List.singleton |> List.singleton
|> Layout.Content.wide |> Layout.Content.wide
|> Layout.standard vi "Small Group Overview" |> Layout.standard viewInfo "Small Group Overview"
open System.IO open System.IO
open PrayerTracker open PrayerTracker
/// View for the small group preferences page /// View for the small group preferences page
let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi = let preferences (model : EditPreferences) (tzs : TimeZone list) ctx viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let l = I18N.forView "SmallGroup/Preferences" let l = I18N.forView "SmallGroup/Preferences"
use sw = new StringWriter () use sw = new StringWriter ()
let raw = rawLocText sw let raw = rawLocText sw
[ style [ _scoped ] [ let vi =
rawText "#expireDays, #daysToKeepNew, #longTermUpdateWeeks, #headingFontSize, #listFontSize, #pageSize { width: 3rem; } #emailFromAddress { width: 20rem; } #fonts { width: 40rem; } @media screen and (max-width: 40rem) { #fonts { width: 100%; } }" viewInfo
|> AppViewInfo.withScopedStyles [
"#expireDays, #daysToKeepNew, #longTermUpdateWeeks, #headingFontSize, #listFontSize, #pageSize { width: 3rem; }"
"#emailFromAddress { width: 20rem; }"
"#fonts { width: 40rem; }"
"@media screen and (max-width: 40rem) { #fonts { width: 100%; } }"
] ]
|> AppViewInfo.withOnLoadScript "PT.smallGroup.preferences.onPageLoad"
form [ _action "/small-group/preferences/save"; _method "post"; _class "pt-center-columns"; Target.content ] [ form [ _action "/small-group/preferences/save"; _method "post"; _class "pt-center-columns"; Target.content ] [
csrfToken ctx csrfToken ctx
fieldset [] [ fieldset [] [
legend [] [ strong [] [ icon "date_range"; rawText " &nbsp;"; locStr s["Dates"] ] ] legend [] [ strong [] [ icon "date_range"; rawText " &nbsp;"; locStr s["Dates"] ] ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "expireDays" ] [ locStr s["Requests Expire After"] ] label [ _for "expireDays" ] [ locStr s["Requests Expire After"] ]
span [] [ span [] [
input [ _type "number" input [ _type "number"
_name (nameof m.ExpireDays) _name (nameof model.ExpireDays)
_id "expireDays" _id "expireDays"
_value (string m.ExpireDays) _value (string model.ExpireDays)
_min "1"; _max "30" _min "1"; _max "30"
_required _required
_autofocus ] _autofocus ]
@ -367,26 +373,26 @@ let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi =
str (s["Days"].Value.ToLower ()) str (s["Days"].Value.ToLower ())
] ]
] ]
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "daysToKeepNew" ] [ locStr s["Requests “New” For"] ] label [ _for "daysToKeepNew" ] [ locStr s["Requests “New” For"] ]
span [] [ span [] [
input [ _type "number" input [ _type "number"
_name (nameof m.DaysToKeepNew) _name (nameof model.DaysToKeepNew)
_id "daysToKeepNew" _id "daysToKeepNew"
_min "1"; _max "30" _min "1"; _max "30"
_value (string m.DaysToKeepNew) _value (string model.DaysToKeepNew)
_required ] _required ]
space; str (s["Days"].Value.ToLower ()) space; str (s["Days"].Value.ToLower ())
] ]
] ]
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "longTermUpdateWeeks" ] [ locStr s["Long-Term Requests Alerted for Update"] ] label [ _for "longTermUpdateWeeks" ] [ locStr s["Long-Term Requests Alerted for Update"] ]
span [] [ span [] [
input [ _type "number" input [ _type "number"
_name (nameof m.LongTermUpdateWeeks) _name (nameof model.LongTermUpdateWeeks)
_id "longTermUpdateWeeks" _id "longTermUpdateWeeks"
_min "1"; _max "30" _min "1"; _max "30"
_value (string m.LongTermUpdateWeeks) _value (string model.LongTermUpdateWeeks)
_required ] _required ]
space; str (s["Weeks"].Value.ToLower ()) space; str (s["Weeks"].Value.ToLower ())
] ]
@ -395,35 +401,35 @@ let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi =
] ]
fieldset [] [ fieldset [] [
legend [] [ strong [] [ icon "sort"; rawText " &nbsp;"; locStr s["Request Sorting"] ] ] legend [] [ strong [] [ icon "sort"; rawText " &nbsp;"; locStr s["Request Sorting"] ] ]
radio (nameof m.RequestSort) "requestSort_D" "D" m.RequestSort radio (nameof model.RequestSort) "requestSort_D" "D" model.RequestSort
label [ _for "requestSort_D" ] [ locStr s["Sort by Last Updated Date"] ] label [ _for "requestSort_D" ] [ locStr s["Sort by Last Updated Date"] ]
rawText " &nbsp; " rawText " &nbsp; "
radio (nameof m.RequestSort) "requestSort_R" "R" m.RequestSort radio (nameof model.RequestSort) "requestSort_R" "R" model.RequestSort
label [ _for "requestSort_R" ] [ locStr s["Sort by Requestor Name"] ] label [ _for "requestSort_R" ] [ locStr s["Sort by Requestor Name"] ]
] ]
fieldset [] [ fieldset [] [
legend [] [ strong [] [ icon "mail_outline"; rawText " &nbsp;"; locStr s["E-mail"] ] ] legend [] [ strong [] [ icon "mail_outline"; rawText " &nbsp;"; locStr s["E-mail"] ] ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "emailFromName" ] [ locStr s["From Name"] ] label [ _for "emailFromName" ] [ locStr s["From Name"] ]
input [ _type "text" input [ _type "text"
_name (nameof m.EmailFromName) _name (nameof model.EmailFromName)
_id "emailFromName" _id "emailFromName"
_value m.EmailFromName _value model.EmailFromName
_required ] _required ]
] ]
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "emailFromAddress" ] [ locStr s["From Address"] ] label [ _for "emailFromAddress" ] [ locStr s["From Address"] ]
input [ _type "email" input [ _type "email"
_name (nameof m.EmailFromAddress) _name (nameof model.EmailFromAddress)
_id "emailFromAddress" _id "emailFromAddress"
_value m.EmailFromAddress _value model.EmailFromAddress
_required ] _required ]
] ]
] ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for (nameof m.DefaultEmailType) ] [ locStr s["E-mail Format"] ] label [ _for (nameof model.DefaultEmailType) ] [ locStr s["E-mail Format"] ]
seq { seq {
"", selectDefault s["Select"].Value "", selectDefault s["Select"].Value
yield! yield!
@ -431,138 +437,140 @@ let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi =
|> Seq.skip 1 |> Seq.skip 1
|> Seq.map (fun typ -> fst typ, (snd typ).Value) |> Seq.map (fun typ -> fst typ, (snd typ).Value)
} }
|> selectList (nameof m.DefaultEmailType) m.DefaultEmailType [ _required ] |> selectList (nameof model.DefaultEmailType) model.DefaultEmailType [ _required ]
] ]
] ]
] ]
fieldset [] [ fieldset [] [
legend [] [ strong [] [ icon "color_lens"; rawText " &nbsp;"; locStr s["Colors"] ]; rawText " ***" ] legend [] [ strong [] [ icon "color_lens"; rawText " &nbsp;"; locStr s["Colors"] ]; rawText " ***" ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _class "pt-center-text" ] [ locStr s["Color of Heading Lines"] ] label [ _class "pt-center-text" ] [ locStr s["Color of Heading Lines"] ]
span [] [ span [] [
radio (nameof m.LineColorType) "lineColorType_Name" "Name" m.LineColorType radio (nameof model.LineColorType) "lineColorType_Name" "Name" model.LineColorType
label [ _for "lineColorType_Name" ] [ locStr s["Named Color"] ] label [ _for "lineColorType_Name" ] [ locStr s["Named Color"] ]
namedColorList (nameof m.LineColor) m.LineColor [ namedColorList (nameof model.LineColor) model.LineColor [
_id "lineColor_Select" _id "lineColor_Select"
if m.LineColor.StartsWith "#" then _disabled ] s if model.LineColor.StartsWith "#" then _disabled ] s
rawText "&nbsp; &nbsp; "; str (s["or"].Value.ToUpper ()) rawText "&nbsp; &nbsp; "; str (s["or"].Value.ToUpper ())
radio (nameof m.LineColorType) "lineColorType_RGB" "RGB" m.LineColorType radio (nameof model.LineColorType) "lineColorType_RGB" "RGB" model.LineColorType
label [ _for "lineColorType_RGB" ] [ locStr s["Custom Color"] ] label [ _for "lineColorType_RGB" ] [ locStr s["Custom Color"] ]
input [ _type "color" input [ _type "color"
_name (nameof m.LineColor) _name (nameof model.LineColor)
_id "lineColor_Color" _id "lineColor_Color"
_value m.LineColor // TODO: convert to hex or skip if named _value model.LineColor // TODO: convert to hex or skip if named
if not (m.LineColor.StartsWith "#") then _disabled ] if not (model.LineColor.StartsWith "#") then _disabled ]
] ]
] ]
] ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _class "pt-center-text" ] [ locStr s["Color of Heading Text"] ] label [ _class "pt-center-text" ] [ locStr s["Color of Heading Text"] ]
span [] [ span [] [
radio (nameof m.HeadingColorType) "headingColorType_Name" "Name" m.HeadingColorType radio (nameof model.HeadingColorType) "headingColorType_Name" "Name" model.HeadingColorType
label [ _for "headingColorType_Name" ] [ locStr s["Named Color"] ] label [ _for "headingColorType_Name" ] [ locStr s["Named Color"] ]
namedColorList (nameof m.HeadingColor) m.HeadingColor [ namedColorList (nameof model.HeadingColor) model.HeadingColor [
_id "headingColor_Select" _id "headingColor_Select"
if m.HeadingColor.StartsWith "#" then _disabled ] s if model.HeadingColor.StartsWith "#" then _disabled ] s
rawText "&nbsp; &nbsp; "; str (s["or"].Value.ToUpper ()) rawText "&nbsp; &nbsp; "; str (s["or"].Value.ToUpper ())
radio (nameof m.HeadingColorType) "headingColorType_RGB" "RGB" m.HeadingColorType radio (nameof model.HeadingColorType) "headingColorType_RGB" "RGB" model.HeadingColorType
label [ _for "headingColorType_RGB" ] [ locStr s["Custom Color"] ] label [ _for "headingColorType_RGB" ] [ locStr s["Custom Color"] ]
input [ _type "color" input [ _type "color"
_name (nameof m.HeadingColor) _name (nameof model.HeadingColor)
_id "headingColor_Color" _id "headingColor_Color"
_value m.HeadingColor // TODO: convert to hex or skip if named _value model.HeadingColor // TODO: convert to hex or skip if named
if not (m.HeadingColor.StartsWith "#") then _disabled ] if not (model.HeadingColor.StartsWith "#") then _disabled ]
] ]
] ]
] ]
] ]
fieldset [] [ fieldset [] [
legend [] [ strong [] [ icon "font_download"; rawText " &nbsp;"; locStr s["Fonts"] ] ] legend [] [ strong [] [ icon "font_download"; rawText " &nbsp;"; locStr s["Fonts"] ] ]
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "fonts" ] [ locStr s["Fonts** for List"] ] label [ _for "fonts" ] [ locStr s["Fonts** for List"] ]
input [ _type "text"; _name (nameof m.Fonts); _id "fonts"; _required; _value m.Fonts ] input [ _type "text"; _name (nameof model.Fonts); _id "fonts"; _required; _value model.Fonts ]
] ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "headingFontSize" ] [ locStr s["Heading Text Size"] ] label [ _for "headingFontSize" ] [ locStr s["Heading Text Size"] ]
input [ _type "number" input [ _type "number"
_name (nameof m.HeadingFontSize) _name (nameof model.HeadingFontSize)
_id "headingFontSize" _id "headingFontSize"
_min "8"; _max "24" _min "8"; _max "24"
_value (string m.HeadingFontSize) _value (string model.HeadingFontSize)
_required ] _required ]
] ]
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "listFontSize" ] [ locStr s["List Text Size"] ] label [ _for "listFontSize" ] [ locStr s["List Text Size"] ]
input [ _type "number" input [ _type "number"
_name (nameof m.ListFontSize) _name (nameof model.ListFontSize)
_id "listFontSize" _id "listFontSize"
_min "8"; _max "24" _min "8"; _max "24"
_value (string m.ListFontSize) _value (string model.ListFontSize)
_required ] _required ]
] ]
] ]
] ]
fieldset [] [ fieldset [] [
legend [] [ strong [] [ icon "settings"; rawText " &nbsp;"; locStr s["Other Settings"] ] ] legend [] [ strong [] [ icon "settings"; rawText " &nbsp;"; locStr s["Other Settings"] ] ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for (nameof m.TimeZone) ] [ locStr s["Time Zone"] ] label [ _for (nameof model.TimeZone) ] [ locStr s["Time Zone"] ]
seq { seq {
"", selectDefault s["Select"].Value "", selectDefault s["Select"].Value
yield! tzs |> List.map (fun tz -> tz.timeZoneId, (TimeZones.name tz.timeZoneId s).Value) yield! tzs |> List.map (fun tz -> tz.timeZoneId, (TimeZones.name tz.timeZoneId s).Value)
} }
|> selectList (nameof m.TimeZone) m.TimeZone [ _required ] |> selectList (nameof model.TimeZone) model.TimeZone [ _required ]
] ]
] ]
div [ _class "pt-field" ] [ div [ _inputField ] [
label [] [ locStr s["Request List Visibility"] ] label [] [ locStr s["Request List Visibility"] ]
span [] [ span [] [
radio (nameof m.Visibility) "viz_Public" (string RequestVisibility.``public``) radio (nameof model.Visibility) "viz_Public" (string RequestVisibility.``public``)
(string m.Visibility) (string model.Visibility)
label [ _for "viz_Public" ] [ locStr s["Public"] ] label [ _for "viz_Public" ] [ locStr s["Public"] ]
rawText " &nbsp;" rawText " &nbsp;"
radio (nameof m.Visibility) "viz_Private" (string RequestVisibility.``private``) radio (nameof model.Visibility) "viz_Private" (string RequestVisibility.``private``)
(string m.Visibility) (string model.Visibility)
label [ _for "viz_Private" ] [ locStr s["Private"] ] label [ _for "viz_Private" ] [ locStr s["Private"] ]
rawText " &nbsp;" rawText " &nbsp;"
radio (nameof m.Visibility) "viz_Password" (string RequestVisibility.passwordProtected) radio (nameof model.Visibility) "viz_Password" (string RequestVisibility.passwordProtected)
(string m.Visibility) (string model.Visibility)
label [ _for "viz_Password" ] [ locStr s["Password Protected"] ] label [ _for "viz_Password" ] [ locStr s["Password Protected"] ]
] ]
] ]
let classSuffix = if m.Visibility = RequestVisibility.passwordProtected then " pt-show" else "" let classSuffix = if model.Visibility = RequestVisibility.passwordProtected then [ "pt-show" ] else []
div [ _id "divClassPassword"; _class $"pt-field-row pt-fadeable{classSuffix}" ] [ div [ _id "divClassPassword"; _fieldRowWith ("pt-fadeable" :: classSuffix) ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "groupPassword" ] [ locStr s["Group Password (Used to Read Online)"] ] label [ _for "groupPassword" ] [ locStr s["Group Password (Used to Read Online)"] ]
input [ _type "text" input [ _type "text"
_name (nameof m.GroupPassword) _name (nameof model.GroupPassword)
_id "groupPassword" _id "groupPassword"
_value (defaultArg m.GroupPassword "") ] _value (defaultArg model.GroupPassword "") ]
] ]
] ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "pageSize" ] [ locStr s["Page Size"] ] label [ _for "pageSize" ] [ locStr s["Page Size"] ]
input [ _type "number" input [ _type "number"
_name (nameof m.PageSize) _name (nameof model.PageSize)
_id "pageSize" _id "pageSize"
_min "10"; _max "255" _min "10"; _max "255"
_value (string m.PageSize) _value (string model.PageSize)
_required ] _required ]
] ]
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for (nameof m.AsOfDate) ] [ locStr s["“As of” Date Display"] ] label [ _for (nameof model.AsOfDate) ] [ locStr s["“As of” Date Display"] ]
ReferenceList.asOfDateList s ReferenceList.asOfDateList s
|> List.map (fun (code, desc) -> code, desc.Value) |> List.map (fun (code, desc) -> code, desc.Value)
|> selectList (nameof m.AsOfDate) m.AsOfDate [ _required ] |> selectList (nameof model.AsOfDate) model.AsOfDate [ _required ]
] ]
] ]
] ]
div [ _class "pt-field-row" ] [ submit [] "save" s["Save Preferences"] ] div [ _fieldRow ] [ submit [] "save" s["Save Preferences"] ]
] ]
|> List.singleton
|> List.append [
p [] [ p [] [
rawText "** " rawText "** "
raw l["List font names, separated by commas."] raw l["List font names, separated by commas."]
@ -575,7 +583,6 @@ let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi =
rawText "*** " rawText "*** "
raw l["If you want a custom color, you may be able to get some ideas (and a list of RGB values for those colors) from the W3 School's <a href=\"http://www.w3schools.com/html/html_colornames.asp\" title=\"HTML Color List - W3 School\">HTML color name list</a>."] raw l["If you want a custom color, you may be able to get some ideas (and a list of RGB values for those colors) from the W3 School's <a href=\"http://www.w3schools.com/html/html_colornames.asp\" title=\"HTML Color List - W3 School\">HTML color name list</a>."]
] ]
script [] [ rawText "PT.onLoad(PT.smallGroup.preferences.onPageLoad)" ]
] ]
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi "Group Preferences" |> Layout.standard vi "Group Preferences"

View File

@ -4,13 +4,13 @@ open Giraffe.ViewEngine
open PrayerTracker.ViewModels open PrayerTracker.ViewModels
/// View for the group assignment page /// View for the group assignment page
let assignGroups m groups curGroups ctx vi = let assignGroups model groups curGroups ctx viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let pageTitle = sprintf "%s %A" m.UserName s["Assign Groups"] let pageTitle = sprintf "%s %A" model.UserName s["Assign Groups"]
form [ _action "/user/small-groups/save"; _method "post"; _class "pt-center-columns"; Target.content ] [ form [ _action "/user/small-groups/save"; _method "post"; _class "pt-center-columns"; Target.content ] [
csrfToken ctx csrfToken ctx
input [ _type "hidden"; _name (nameof m.UserId); _value (flatGuid m.UserId) ] input [ _type "hidden"; _name (nameof model.UserId); _value (flatGuid model.UserId) ]
input [ _type "hidden"; _name (nameof m.UserName); _value m.UserName ] input [ _type "hidden"; _name (nameof model.UserName); _value model.UserName ]
table [ _class "pt-table" ] [ table [ _class "pt-table" ] [
thead [] [ thead [] [
tr [] [ tr [] [
@ -24,7 +24,7 @@ let assignGroups m groups curGroups ctx vi =
tr [] [ tr [] [
td [] [ td [] [
input [ _type "checkbox" input [ _type "checkbox"
_name (nameof m.SmallGroups) _name (nameof model.SmallGroups)
_id inputId _id inputId
_value grpId _value grpId
if List.contains grpId curGroups then _checked ] if List.contains grpId curGroups then _checked ]
@ -33,43 +33,53 @@ let assignGroups m groups curGroups ctx vi =
]) ])
|> tbody [] |> tbody []
] ]
div [ _class "pt-field-row" ] [ submit [] "save" s["Save Group Assignments"] ] div [ _fieldRow ] [ submit [] "save" s["Save Group Assignments"] ]
] ]
|> List.singleton |> List.singleton
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi pageTitle |> Layout.standard viewInfo pageTitle
/// View for the password change page /// View for the password change page
let changePassword ctx vi = let changePassword ctx viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let m = { OldPassword = ""; NewPassword = ""; NewPasswordConfirm = "" } let model = { OldPassword = ""; NewPassword = ""; NewPasswordConfirm = "" }
[ p [ _class "pt-center-text" ] [ let vi =
AppViewInfo.withScopedStyles [ "#oldPassword, #newPassword, #newPasswordConfirm { width: 10rem; }"] viewInfo
p [ _class "pt-center-text" ] [
locStr s["To change your password, enter your current password in the specified box below, then enter your new password twice."] locStr s["To change your password, enter your current password in the specified box below, then enter your new password twice."]
] ]
style [ _scoped ] [ rawText "#oldPassword, #newPassword, #newPasswordConfirm { width: 10rem; } "] |> List.singleton
|> List.append [
form [ _action "/user/password/change" form [ _action "/user/password/change"
_method "post" _method "post"
_onsubmit $"""return PT.compareValidation('newPassword','newPasswordConfirm','%A{s["The passwords do not match"]}')""" _onsubmit $"""return PT.compareValidation('newPassword','newPasswordConfirm','%A{s["The passwords do not match"]}')"""
Target.content ] [ Target.content ] [
csrfToken ctx csrfToken ctx
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "oldPassword" ] [ locStr s["Current Password"] ] label [ _for "oldPassword" ] [ locStr s["Current Password"] ]
input [ _type "password"; _name (nameof m.OldPassword); _id "oldPassword"; _required; _autofocus ] input [ _type "password"
_name (nameof model.OldPassword)
_id "oldPassword"
_required
_autofocus ]
] ]
] ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "newPassword" ] [ locStr s["New Password Twice"] ] label [ _for "newPassword" ] [ locStr s["New Password Twice"] ]
input [ _type "password"; _name (nameof m.NewPassword); _id "newPassword"; _required ] input [ _type "password"; _name (nameof model.NewPassword); _id "newPassword"; _required ]
] ]
div [ _class "pt-field" ] [ div [ _inputField ] [
label [] [ rawText "&nbsp;" ] label [] [ rawText "&nbsp;" ]
input [ _type "password"; _name (nameof m.NewPasswordConfirm); _id "newPasswordConfirm"; _required ] input [ _type "password"
_name (nameof model.NewPasswordConfirm)
_id "newPasswordConfirm"
_required ]
] ]
] ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
submit [ _onclick "document.getElementById('newPasswordConfirm').setCustomValidity('')" ] "done" submit [ _onclick "document.getElementById('newPasswordConfirm').setCustomValidity('')" ] "done"
s["Change Your Password"] s["Change Your Password"]
] ]
@ -80,102 +90,110 @@ let changePassword ctx vi =
/// View for the edit user page /// View for the edit user page
let edit (m : EditUser) ctx vi = let edit (model : EditUser) ctx viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let pageTitle = if m.IsNew then "Add a New User" else "Edit User" let pageTitle = if model.IsNew then "Add a New User" else "Edit User"
let pwPlaceholder = s[if m.IsNew then "" else "No change"].Value let pwPlaceholder = s[if model.IsNew then "" else "No change"].Value
[ style [ _scoped ] let vi =
[ rawText "#firstName, #lastName, #password, #passwordConfirm { width: 10rem; } #email { width: 20rem; } " ] viewInfo
|> AppViewInfo.withScopedStyles [
"#firstName, #lastName, #password, #passwordConfirm { width: 10rem; }"
"#email { width: 20rem; }"
]
|> AppViewInfo.withOnLoadScript $"PT.user.edit.onPageLoad({(string model.IsNew).ToLowerInvariant ()})"
form [ _action "/user/edit/save" form [ _action "/user/edit/save"
_method "post" _method "post"
_class "pt-center-columns" _class "pt-center-columns"
_onsubmit $"""return PT.compareValidation('password','passwordConfirm','%A{s["The passwords do not match"]}')""" _onsubmit $"""return PT.compareValidation('password','passwordConfirm','%A{s["The passwords do not match"]}')"""
Target.content ] [ Target.content ] [
csrfToken ctx csrfToken ctx
input [ _type "hidden"; _name (nameof m.UserId); _value (flatGuid m.UserId) ] input [ _type "hidden"; _name (nameof model.UserId); _value (flatGuid model.UserId) ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "firstName" ] [ locStr s["First Name"] ] label [ _for "firstName" ] [ locStr s["First Name"] ]
input [ _type "text" input [ _type "text"
_name (nameof m.FirstName) _name (nameof model.FirstName)
_id "firstName" _id "firstName"
_value m.FirstName _value model.FirstName
_required _required
_autofocus ] _autofocus ]
] ]
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "lastName" ] [ locStr s["Last Name"] ] label [ _for "lastName" ] [ locStr s["Last Name"] ]
input [ _type "text"; _name (nameof m.LastName); _id "lastName"; _value m.LastName; _required ] input [ _type "text"; _name (nameof model.LastName); _id "lastName"; _value model.LastName; _required ]
] ]
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "email" ] [ locStr s["E-mail Address"] ] label [ _for "email" ] [ locStr s["E-mail Address"] ]
input [ _type "email"; _name (nameof m.Email); _id "email"; _value m.Email; _required ] input [ _type "email"; _name (nameof model.Email); _id "email"; _value model.Email; _required ]
] ]
] ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "password" ] [ locStr s["Password"] ] label [ _for "password" ] [ locStr s["Password"] ]
input [ _type "password"; _name (nameof m.Password); _id "password"; _placeholder pwPlaceholder ] input [ _type "password"; _name (nameof model.Password); _id "password"; _placeholder pwPlaceholder ]
] ]
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "passwordConfirm" ] [ locStr s["Password Again"] ] label [ _for "passwordConfirm" ] [ locStr s["Password Again"] ]
input [ _type "password" input [ _type "password"
_name (nameof m.PasswordConfirm) _name (nameof model.PasswordConfirm)
_id "passwordConfirm" _id "passwordConfirm"
_placeholder pwPlaceholder ] _placeholder pwPlaceholder ]
] ]
] ]
div [ _class "pt-checkbox-field" ] [ div [ _checkboxField ] [
input [ _type "checkbox" input [ _type "checkbox"
_name (nameof m.IsAdmin) _name (nameof model.IsAdmin)
_id "isAdmin" _id "isAdmin"
_value "True" _value "True"
if defaultArg m.IsAdmin false then _checked ] if defaultArg model.IsAdmin false then _checked ]
label [ _for "isAdmin" ] [ locStr s["This user is a PrayerTracker administrator"] ] label [ _for "isAdmin" ] [ locStr s["This user is a PrayerTracker administrator"] ]
] ]
div [ _class "pt-field-row" ] [ submit [] "save" s["Save User"] ] div [ _fieldRow ] [ submit [] "save" s["Save User"] ]
]
script [] [ rawText $"PT.onLoad(PT.user.edit.onPageLoad({(string m.IsNew).ToLowerInvariant ()}))" ]
] ]
|> List.singleton
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi pageTitle |> Layout.standard vi pageTitle
/// View for the user log on page /// View for the user log on page
let logOn (m : UserLogOn) groups ctx vi = let logOn (model : UserLogOn) groups ctx viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let vi = AppViewInfo.withScopedStyles [ "#email { width: 20rem; }" ] viewInfo
form [ _action "/user/log-on"; _method "post"; _class "pt-center-columns"; Target.body ] [ form [ _action "/user/log-on"; _method "post"; _class "pt-center-columns"; Target.body ] [
style [ _scoped ] [ rawText "#email { width: 20rem; }" ]
csrfToken ctx csrfToken ctx
input [ _type "hidden"; _name (nameof m.RedirectUrl); _value (defaultArg m.RedirectUrl "") ] input [ _type "hidden"; _name (nameof model.RedirectUrl); _value (defaultArg model.RedirectUrl "") ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "email"] [ locStr s["E-mail Address"] ] label [ _for "email"] [ locStr s["E-mail Address"] ]
input [ _type "email"; _name (nameof m.Email); _id "email"; _value m.Email; _required; _autofocus ] input [ _type "email"
_name (nameof model.Email)
_id "email"
_value model.Email
_required
_autofocus ]
] ]
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for "password" ] [ locStr s["Password"] ] label [ _for "password" ] [ locStr s["Password"] ]
input [ _type "password" input [ _type "password"
_name (nameof m.Password) _name (nameof model.Password)
_id "password" _id "password"
_placeholder $"""({s["Case-Sensitive"].Value.ToLower ()})""" _placeholder $"""({s["Case-Sensitive"].Value.ToLower ()})"""
_required ] _required ]
] ]
] ]
div [ _class "pt-field-row" ] [ div [ _fieldRow ] [
div [ _class "pt-field" ] [ div [ _inputField ] [
label [ _for (nameof m.SmallGroupId) ] [ locStr s["Group"] ] label [ _for (nameof model.SmallGroupId) ] [ locStr s["Group"] ]
seq { "", selectDefault s["Select Group"].Value; yield! groups } seq { "", selectDefault s["Select Group"].Value; yield! groups }
|> selectList (nameof m.SmallGroupId) "" [ _required ] |> selectList (nameof model.SmallGroupId) "" [ _required ]
] ]
] ]
div [ _class "pt-checkbox-field" ] [ div [ _checkboxField ] [
input [ _type "checkbox"; _name (nameof m.RememberMe); _id "rememberMe"; _value "True" ] input [ _type "checkbox"; _name (nameof model.RememberMe); _id "rememberMe"; _value "True" ]
label [ _for "rememberMe" ] [ locStr s["Remember Me"] ] label [ _for "rememberMe" ] [ locStr s["Remember Me"] ]
br [] br []
small [] [ em [] [ str $"""({s["Requires Cookies"].Value.ToLower ()})""" ] ] small [] [ em [] [ str $"""({s["Requires Cookies"].Value.ToLower ()})""" ] ]
] ]
div [ _class "pt-field-row" ] [ submit [] "account_circle" s["Log On"] ] div [ _fieldRow ] [ submit [] "account_circle" s["Log On"] ]
] ]
|> List.singleton |> List.singleton
|> Layout.Content.standard |> Layout.Content.standard
@ -185,7 +203,7 @@ let logOn (m : UserLogOn) groups ctx vi =
open PrayerTracker.Entities open PrayerTracker.Entities
/// View for the user maintenance page /// View for the user maintenance page
let maintain (users : User list) ctx vi = let maintain (users : User list) ctx viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let usrTbl = let usrTbl =
match users with match users with
@ -237,4 +255,4 @@ let maintain (users : User list) ctx vi =
form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ] form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ]
] ]
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi "Maintain Users" |> Layout.standard viewInfo "Maintain Users"

View File

@ -147,7 +147,14 @@ type AppViewInfo =
/// The layout with which the content will be rendered /// The layout with which the content will be rendered
Layout : LayoutType Layout : LayoutType
/// Scoped styles for this view
ScopedStyle : string list
/// A JavaScript function to run on page load
OnLoadScript : string option
} }
// TODO: add onload script option to this, modify layout to add it
/// Support for the AppViewInfo type /// Support for the AppViewInfo type
module AppViewInfo = module AppViewInfo =
@ -163,8 +170,18 @@ module AppViewInfo =
User = None User = None
Group = None Group = None
Layout = FullPage Layout = FullPage
ScopedStyle = []
OnLoadScript = None
} }
/// Add scoped styles to the given view info object
let withScopedStyles styles viewInfo =
{ viewInfo with ScopedStyle = styles }
/// Add an onload action to the given view info object
let withOnLoadScript script viewInfo =
{ viewInfo with OnLoadScript = Some script }
/// Form for sending a small group or system-wide announcement /// Form for sending a small group or system-wide announcement
[<CLIMutable; NoComparison; NoEquality>] [<CLIMutable; NoComparison; NoEquality>]

View File

@ -2,7 +2,7 @@
* This file contains a library of common functions used throughout the PrayerTracker website, as well as specific * This file contains a library of common functions used throughout the PrayerTracker website, as well as specific
* functions used on pages throughout the site. * functions used on pages throughout the site.
*/ */
const PT = { this.PT = {
/** /**
* Open a window with help * Open a window with help