Version 8 #43
| @ -5,57 +5,62 @@ open PrayerTracker.Entities | ||||
| open PrayerTracker.ViewModels | ||||
| 
 | ||||
| /// View for the church edit page | ||||
| let edit (m : EditChurch) ctx vi = | ||||
|     let pageTitle = if m.IsNew then "Add a New Church" else "Edit Church" | ||||
| let edit (model : EditChurch) ctx viewInfo = | ||||
|     let pageTitle = if model.IsNew then "Add a New Church" else "Edit Church" | ||||
|     let s         = I18N.localizer.Force () | ||||
|     [   form [ _action "/church/save"; _method "post"; _class "pt-center-columns"; Target.content ] [ | ||||
|             style [ _scoped ] [ | ||||
|                 rawText "#name { width: 20rem; } #city { width: 10rem; } #st { width: 3rem; } #interfaceAddress { width: 30rem; }" | ||||
|             ] | ||||
|             csrfToken ctx | ||||
|             input [ _type "hidden"; _name (nameof m.ChurchId); _value (flatGuid m.ChurchId) ] | ||||
|             div [ _class "pt-field-row" ] [ | ||||
|                 div [ _class "pt-field" ] [ | ||||
|                     label [ _for "name" ] [ locStr s["Church Name"] ] | ||||
|                     input [ _type "text"; _name (nameof m.Name); _id "name"; _required; _autofocus; _value m.Name ] | ||||
|                 ] | ||||
|                 div [ _class "pt-field" ] [ | ||||
|                     label [ _for "City"] [ locStr s["City"] ] | ||||
|                     input [ _type "text"; _name (nameof m.City); _id "city"; _required; _value m.City ] | ||||
|                 ] | ||||
|                 div [ _class "pt-field" ] [ | ||||
|                     label [ _for "state" ] [ locStr s["State or Province"] ] | ||||
|                     input [ _type      "text" | ||||
|                             _name      (nameof m.State) | ||||
|                             _id        "state" | ||||
|                             _minlength "2"; _maxlength "2" | ||||
|                             _value     m.State | ||||
|                             _required ] | ||||
|                 ] | ||||
|             ] | ||||
|             div [ _class "pt-field-row" ] [ | ||||
|                 div [ _class "pt-checkbox-field" ] [ | ||||
|                     input [ _type  "checkbox" | ||||
|                             _name  (nameof m.HasInterface) | ||||
|                             _id    "hasInterface" | ||||
|                             _value "True" | ||||
|                             if defaultArg m.HasInterface false then _checked ] | ||||
|                     label [ _for "hasInterface" ] [ locStr s["Has an interface with Virtual Prayer Room"] ] | ||||
|                 ] | ||||
|             ] | ||||
|             div [ _class "pt-field-row pt-fadeable"; _id "divInterfaceAddress" ] [ | ||||
|                 div [ _class "pt-field" ] [ | ||||
|                     label [ _for "interfaceAddress" ] [ locStr s["VPR Interface URL"] ] | ||||
|                     input [ _type  "url" | ||||
|                             _name  (nameof m.InterfaceAddress) | ||||
|                             _id    "interfaceAddress"; | ||||
|                             _value (defaultArg m.InterfaceAddress "") ] | ||||
|                 ] | ||||
|             ] | ||||
|             div [ _class "pt-field-row" ] [ submit [] "save" s["Save Church"] ] | ||||
|     let vi        = | ||||
|         viewInfo | ||||
|         |> AppViewInfo.withScopedStyles [ | ||||
|             "#name { width: 20rem; }" | ||||
|             "#city { width: 10rem; }" | ||||
|             "#st { width: 3rem; }" | ||||
|             "#interfaceAddress { width: 30rem; }" | ||||
|         ] | ||||
|         script [] [ rawText "PT.onLoad(PT.church.edit.onPageLoad)" ] | ||||
|         |> AppViewInfo.withOnLoadScript "PT.church.edit.onPageLoad" | ||||
|     form [ _action "/church/save"; _method "post"; _class "pt-center-columns"; Target.content ] [ | ||||
|         csrfToken ctx | ||||
|         input [ _type "hidden"; _name (nameof model.ChurchId); _value (flatGuid model.ChurchId) ] | ||||
|         div [ _fieldRow ] [ | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for "name" ] [ locStr s["Church Name"] ] | ||||
|                 input [ _type "text"; _name (nameof model.Name); _id "name"; _required; _autofocus; _value model.Name ] | ||||
|             ] | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for "City"] [ locStr s["City"] ] | ||||
|                 input [ _type "text"; _name (nameof model.City); _id "city"; _required; _value model.City ] | ||||
|             ] | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for "state" ] [ locStr s["State or Province"] ] | ||||
|                 input [ _type      "text" | ||||
|                         _name      (nameof model.State) | ||||
|                         _id        "state" | ||||
|                         _minlength "2"; _maxlength "2" | ||||
|                         _value     model.State | ||||
|                         _required ] | ||||
|             ] | ||||
|         ] | ||||
|         div [ _fieldRow ] [ | ||||
|             div [ _checkboxField ] [ | ||||
|                 input [ _type  "checkbox" | ||||
|                         _name  (nameof model.HasInterface) | ||||
|                         _id    "hasInterface" | ||||
|                         _value "True" | ||||
|                         if defaultArg model.HasInterface false then _checked ] | ||||
|                 label [ _for "hasInterface" ] [ locStr s["Has an interface with Virtual Prayer Room"] ] | ||||
|             ] | ||||
|         ] | ||||
|         div [ _fieldRowWith [ "pt-fadeable" ]; _id "divInterfaceAddress" ] [ | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for "interfaceAddress" ] [ locStr s["VPR Interface URL"] ] | ||||
|                 input [ _type  "url" | ||||
|                         _name  (nameof model.InterfaceAddress) | ||||
|                         _id    "interfaceAddress"; | ||||
|                         _value (defaultArg model.InterfaceAddress "") ] | ||||
|             ] | ||||
|         ] | ||||
|         div [ _fieldRow ] [ submit [] "save" s["Save Church"] ] | ||||
|     ] | ||||
|     |> List.singleton | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard vi pageTitle | ||||
| 
 | ||||
|  | ||||
| @ -119,6 +119,25 @@ let _onsubmit = attr "onsubmit" | ||||
| /// A "rel='noopener'" attribute | ||||
| 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 | ||||
| let renderHtmlNode = RenderView.AsString.htmlNode | ||||
| 
 | ||||
|  | ||||
| @ -6,7 +6,7 @@ open Giraffe.ViewEngine | ||||
| open PrayerTracker.ViewModels | ||||
| 
 | ||||
| /// The error page | ||||
| let error code vi = | ||||
| let error code viewInfo = | ||||
|     let s   = I18N.localizer.Force () | ||||
|     let l   = I18N.forView "Home/Error" | ||||
|     use sw  = new StringWriter () | ||||
| @ -37,7 +37,7 @@ let error code vi = | ||||
|                   _alt   $"""%A{s["PrayerTracker"]} %A{s["from Bit Badger Solutions"]}""" | ||||
|                   _title $"""%A{s["PrayerTracker"]} %A{s["from Bit Badger Solutions"]}""" | ||||
|                   _style "vertical-align:text-bottom;" ] | ||||
|             str vi.Version | ||||
|             str viewInfo.Version | ||||
|         ] | ||||
|     ] | ||||
|     |> div [] | ||||
| @ -45,7 +45,7 @@ let error code vi = | ||||
| 
 | ||||
| 
 | ||||
| /// The home page | ||||
| let index vi = | ||||
| let index viewInfo = | ||||
|     let s   = I18N.localizer.Force () | ||||
|     let l   = I18N.forView "Home/Index" | ||||
|     use sw  = new StringWriter () | ||||
| @ -118,11 +118,11 @@ let index vi = | ||||
|         ] | ||||
|     ] | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard vi "Welcome!" | ||||
|     |> Layout.standard viewInfo "Welcome!" | ||||
| 
 | ||||
| 
 | ||||
| /// Privacy Policy page | ||||
| let privacyPolicy vi = | ||||
| let privacyPolicy viewInfo = | ||||
|     let s   = I18N.localizer.Force () | ||||
|     let l   = I18N.forView "Home/PrivacyPolicy" | ||||
|     use sw  = new StringWriter () | ||||
| @ -190,11 +190,11 @@ let privacyPolicy vi = | ||||
|         ] | ||||
|     ] | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard vi "Privacy Policy" | ||||
|     |> Layout.standard viewInfo "Privacy Policy" | ||||
| 
 | ||||
| 
 | ||||
| /// Terms of Service page | ||||
| let termsOfService vi = | ||||
| let termsOfService viewInfo = | ||||
|     let s      = I18N.localizer.Force () | ||||
|     let l      = I18N.forView "Home/TermsOfService" | ||||
|     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] ] | ||||
|     ] | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard vi "Terms of Service" | ||||
|     |> Layout.standard viewInfo "Terms of Service" | ||||
| 
 | ||||
| 
 | ||||
| /// View for unauthorized page | ||||
| let unauthorized vi = | ||||
| let unauthorized viewInfo = | ||||
|     let s   = I18N.localizer.Force () | ||||
|     let l   = I18N.forView "Home/Unauthorized" | ||||
|     use sw  = new StringWriter () | ||||
| @ -256,4 +256,4 @@ let unauthorized vi = | ||||
|         ] | ||||
|     ] | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard vi "Unauthorized Access" | ||||
|     |> Layout.standard viewInfo "Unauthorized Access" | ||||
|  | ||||
| @ -293,10 +293,16 @@ let private contentSection viewInfo title (content : XmlNode) = [ | ||||
|     Navigation.identity viewInfo | ||||
|     renderPageTitle viewInfo title | ||||
|     yield! messages viewInfo | ||||
|     match viewInfo.ScopedStyle with | ||||
|     | [] -> () | ||||
|     | styles -> style [ _scoped ] (styles |> List.map (fun it -> rawText $"{it};")) | ||||
|     content | ||||
|     htmlFooter viewInfo | ||||
|     for jsFile in viewInfo.Script do | ||||
|         script [ _src $"/js/{jsFile}.js" ] [] | ||||
|     match viewInfo.OnLoadScript with | ||||
|     | Some onLoad -> script [] [ rawText $"PT.onLoad({onLoad}" ] | ||||
|     | None -> () | ||||
| ] | ||||
| 
 | ||||
| /// The HTML head element for partial responses | ||||
|  | ||||
| @ -11,79 +11,80 @@ open PrayerTracker.Entities | ||||
| open PrayerTracker.ViewModels | ||||
| 
 | ||||
| /// 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 pageTitle = if m.IsNew then "Add a New Request" else "Edit Request" | ||||
|     [   form [ _action "/prayer-request/save"; _method "post"; _class "pt-center-columns"; Target.content ] [ | ||||
|             csrfToken ctx | ||||
|             input [ _type "hidden"; _name (nameof m.RequestId); _value (flatGuid m.RequestId) ] | ||||
|             div [ _class "pt-field-row" ] [ | ||||
|                 div [ _class "pt-field" ] [ | ||||
|                     label [ _for (nameof m.RequestType) ] [ locStr s["Request Type"] ] | ||||
|                     ReferenceList.requestTypeList s | ||||
|                     |> Seq.ofList | ||||
|                     |> Seq.map (fun (typ, desc) -> typ.code, desc.Value) | ||||
|                     |> selectList (nameof m.RequestType) m.RequestType [ _required; _autofocus ] | ||||
|     let pageTitle = if model.IsNew then "Add a New Request" else "Edit Request" | ||||
|     let vi        = AppViewInfo.withOnLoadScript "PT.initCKEditor" viewInfo | ||||
|     form [ _action "/prayer-request/save"; _method "post"; _class "pt-center-columns"; Target.content ] [ | ||||
|         csrfToken ctx | ||||
|         input [ _type "hidden"; _name (nameof model.RequestId); _value (flatGuid model.RequestId) ] | ||||
|         div [ _fieldRow ] [ | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for (nameof model.RequestType) ] [ locStr s["Request Type"] ] | ||||
|                 ReferenceList.requestTypeList s | ||||
|                 |> Seq.ofList | ||||
|                 |> Seq.map (fun (typ, desc) -> typ.code, desc.Value) | ||||
|                 |> selectList (nameof model.RequestType) model.RequestType [ _required; _autofocus ] | ||||
|             ] | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for "requestor" ] [ locStr s["Requestor / Subject"] ] | ||||
|                 input [ _type  "text" | ||||
|                         _id    "requestor" | ||||
|                         _name  (nameof model.Requestor) | ||||
|                         _value (defaultArg model.Requestor "") ] | ||||
|             ] | ||||
|             if model.IsNew then | ||||
|                 div [ _inputField ] [ | ||||
|                     label [ _for "enteredDate" ] [ locStr s["Date"] ] | ||||
|                     input [ _type "date"; _name (nameof model.EnteredDate); _id "enteredDate"; _placeholder today ] | ||||
|                 ] | ||||
|                 div [ _class "pt-field" ] [ | ||||
|                     label [ _for "requestor" ] [ locStr s["Requestor / Subject"] ] | ||||
|                     input [ _type  "text" | ||||
|                             _id    "requestor" | ||||
|                             _name  (nameof m.Requestor) | ||||
|                             _value (defaultArg m.Requestor "") ] | ||||
|                 ] | ||||
|                 if m.IsNew then | ||||
|                     div [ _class "pt-field" ] [ | ||||
|                         label [ _for "enteredDate" ] [ locStr s["Date"] ] | ||||
|                         input [ _type "date"; _name (nameof m.EnteredDate); _id "enteredDate"; _placeholder today ] | ||||
|             else | ||||
|                 // TODO: do these need to be nested like this? | ||||
|                 div [ _inputField ] [ | ||||
|                     div [ _checkboxField ] [ | ||||
|                         br [] | ||||
|                         input [ _type  "checkbox" | ||||
|                                 _name  (nameof model.SkipDateUpdate) | ||||
|                                 _id    "skipDateUpdate" | ||||
|                                 _value "True" ] | ||||
|                         label [ _for "skipDateUpdate" ] [ locStr s["Check to not update the date"] ] | ||||
|                         br [] | ||||
|                         small [] [ em [] [ str (s["Typo Corrections"].Value.ToLower ()); rawText ", etc." ] ] | ||||
|                     ] | ||||
|                 else | ||||
|                     div [ _class "pt-field" ] [ | ||||
|                         div [ _class "pt-checkbox-field" ] [ | ||||
|                             br [] | ||||
|                             input [ _type  "checkbox" | ||||
|                                     _name  (nameof m.SkipDateUpdate) | ||||
|                                     _id    "skipDateUpdate" | ||||
|                                     _value "True" ] | ||||
|                             label [ _for "skipDateUpdate" ] [ locStr s["Check to not update the date"] ] | ||||
|                             br [] | ||||
|                             small [] [ em [] [ str (s["Typo Corrections"].Value.ToLower ()); rawText ", etc." ] ] | ||||
|                         ] | ||||
|                     ] | ||||
|             ] | ||||
|             div [ _class "pt-field-row" ] [ | ||||
|                 div [ _class "pt-field" ] [ | ||||
|                     label [] [ locStr s["Expiration"] ] | ||||
|                     ReferenceList.expirationList s (not m.IsNew) | ||||
|                     |> List.map (fun exp -> | ||||
|                         let radioId = $"expiration_{fst exp}" | ||||
|                         span [ _class "text-nowrap" ] [ | ||||
|                             radio (nameof m.Expiration) radioId (fst exp) m.Expiration | ||||
|                             label [ _for radioId ] [ locStr (snd exp) ] | ||||
|                             rawText "     " | ||||
|                         ]) | ||||
|                     |> div [ _class "pt-center-text" ] | ||||
|                 ] | ||||
|             ] | ||||
|             div [ _class "pt-field-row" ] [ | ||||
|                 div [ _class "pt-field pt-editor" ] [ | ||||
|                     label [ _for "text" ] [ locStr s["Request"] ] | ||||
|                     textarea [ _name (nameof m.Text); _id "text" ] [ str m.Text ] | ||||
|                 ] | ||||
|             ] | ||||
|             div [ _class "pt-field-row" ] [ submit [] "save" s["Save Request"] ] | ||||
|         ] | ||||
|         script [] [ rawText "PT.onLoad(PT.initCKEditor)" ] | ||||
|         div [ _fieldRow ] [ | ||||
|             div [ _inputField ] [ | ||||
|                 label [] [ locStr s["Expiration"] ] | ||||
|                 ReferenceList.expirationList s (not model.IsNew) | ||||
|                 |> List.map (fun exp -> | ||||
|                     let radioId = $"expiration_{fst exp}" | ||||
|                     span [ _class "text-nowrap" ] [ | ||||
|                         radio (nameof model.Expiration) radioId (fst exp) model.Expiration | ||||
|                         label [ _for radioId ] [ locStr (snd exp) ] | ||||
|                         rawText "     " | ||||
|                     ]) | ||||
|                 |> div [ _class "pt-center-text" ] | ||||
|             ] | ||||
|         ] | ||||
|         div [ _fieldRow ] [ | ||||
|             div [ _inputFieldWith [ "pt-editor" ] ] [ | ||||
|                 label [ _for "text" ] [ locStr s["Request"] ] | ||||
|                 textarea [ _name (nameof model.Text); _id "text" ] [ str model.Text ] | ||||
|             ] | ||||
|         ] | ||||
|         div [ _fieldRow ] [ submit [] "save" s["Save Request"] ] | ||||
|     ] | ||||
|     |> List.singleton | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard vi pageTitle | ||||
| 
 | ||||
| /// View for the request e-mail results page | ||||
| let email m vi = | ||||
| let email model viewInfo = | ||||
|     let s         = I18N.localizer.Force () | ||||
|     let pageTitle = $"""{s["Prayer Requests"].Value} • {m.SmallGroup.name}""" | ||||
|     let prefs     = m.SmallGroup.preferences | ||||
|     let addresses = m.Recipients |> List.map (fun mbr -> $"{mbr.memberName} <{mbr.email}>") |> String.concat ", " | ||||
|     let pageTitle = $"""{s["Prayer Requests"].Value} • {model.SmallGroup.name}""" | ||||
|     let prefs     = model.SmallGroup.preferences | ||||
|     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;" ] [ | ||||
|             locStr s["The request list was sent to the following people, via individual e-mails"] | ||||
|             rawText ":" | ||||
| @ -91,27 +92,27 @@ let email m vi = | ||||
|             small [] [ str addresses ] | ||||
|         ] | ||||
|         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 [] | ||||
|         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.standard vi pageTitle | ||||
|     |> Layout.standard viewInfo pageTitle | ||||
| 
 | ||||
| 
 | ||||
| /// View for a small group's public prayer request list | ||||
| let list (m : RequestList) vi = | ||||
| let list (model : RequestList) viewInfo = | ||||
|     [   br [] | ||||
|         I18N.localizer.Force () |> (m.AsHtml >> rawText)  | ||||
|         I18N.localizer.Force () |> (model.AsHtml >> rawText)  | ||||
|     ] | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard vi "View Request List" | ||||
|     |> Layout.standard viewInfo "View Request List" | ||||
| 
 | ||||
| 
 | ||||
| /// 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 l   = I18N.forView "Requests/Lists" | ||||
|     use sw  = new StringWriter () | ||||
| @ -154,17 +155,17 @@ let lists (groups : SmallGroup list) vi = | ||||
|             ] | ||||
|     ] | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard vi "Request Lists" | ||||
|     |> Layout.standard viewInfo "Request Lists" | ||||
| 
 | ||||
| 
 | ||||
| /// 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 l     = I18N.forView "Requests/Maintain" | ||||
|     use sw    = new StringWriter () | ||||
|     let raw   = rawLocText sw | ||||
|     let now   = m.SmallGroup.localDateNow (ctx.GetService<IClock> ()) | ||||
|     let prefs = m.SmallGroup.preferences | ||||
|     let now   = model.SmallGroup.localDateNow (ctx.GetService<IClock> ()) | ||||
|     let prefs = model.SmallGroup.preferences | ||||
|     let types = ReferenceList.requestTypeList s |> Map.ofList | ||||
|     let updReq (req : PrayerRequest) = | ||||
|         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 "") | ||||
|     /// Iterate the sequence once, before we render, so we can get the count of it at the top of the table | ||||
|     let requests = | ||||
|         m.Requests | ||||
|         model.Requests | ||||
|         |> List.map (fun req -> | ||||
|             let reqId     = flatGuid req.prayerRequestId | ||||
|             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 ] [ | ||||
|                 icon "list"; rawText "  "; locStr s["View Prayer Request List"] | ||||
|             ] | ||||
|             match m.SearchTerm with | ||||
|             match model.SearchTerm with | ||||
|             | Some _ -> | ||||
|                 rawText "       " | ||||
|                 a [ _href "/prayer-requests"; _title l["Clear Search Criteria"].Value ] [ | ||||
| @ -240,7 +241,7 @@ let maintain (m : MaintainRequests) (ctx : HttpContext) vi = | ||||
|             input [ _type "text" | ||||
|                     _name "search" | ||||
|                     _placeholder l["Search requests..."].Value | ||||
|                     _value (defaultArg m.SearchTerm "") | ||||
|                     _value (defaultArg model.SearchTerm "") | ||||
|                   ] | ||||
|             space | ||||
|             submit [] "search" s["Search"] | ||||
| @ -264,22 +265,22 @@ let maintain (m : MaintainRequests) (ctx : HttpContext) vi = | ||||
|             ] | ||||
|         div [ _class "pt-center-text" ] [ | ||||
|             br [] | ||||
|             match m.OnlyActive with | ||||
|             match model.OnlyActive with | ||||
|             | Some true -> | ||||
|                 raw l["Inactive requests are currently not shown"] | ||||
|                 br [] | ||||
|                 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"] | ||||
|                     br [] | ||||
|                     a [ _href "/prayer-requests" ] [ raw l["Do Not Show Inactive Requests"] ] | ||||
|                     br [] | ||||
|                     br [] | ||||
|                 let search = [ match m.SearchTerm with Some s -> "search", s | None -> () ] | ||||
|                 let pg     = defaultArg m.PageNbr 1 | ||||
|                 let search = [ match model.SearchTerm with Some s -> "search", s | None -> () ] | ||||
|                 let pg     = defaultArg model.PageNbr 1 | ||||
|                 let url    = | ||||
|                     match m.OnlyActive with Some true | None -> "" | _ -> "/inactive" | ||||
|                     match model.OnlyActive with Some true | None -> "" | _ -> "/inactive" | ||||
|                     |> sprintf "/prayer-requests%s" | ||||
|                 match pg with | ||||
|                 | 1 -> () | ||||
| @ -288,7 +289,7 @@ let maintain (m : MaintainRequests) (ctx : HttpContext) vi = | ||||
|                     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"] ] | ||||
|                 rawText "     " | ||||
|                 match requests.Length = m.SmallGroup.preferences.pageSize with | ||||
|                 match requests.Length = model.SmallGroup.preferences.pageSize with | ||||
|                 | true -> | ||||
|                     a [ _href (makeUrl url (("page", string (pg + 1)) :: search)) ] [ | ||||
|                         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 ] | ||||
|     ] | ||||
|     |> 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 | ||||
| let print m version = | ||||
| let print model version = | ||||
|     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}""" | ||||
|     article [] [ | ||||
|         rawText (m.AsHtml s) | ||||
|         rawText (model.AsHtml s) | ||||
|         br [] | ||||
|         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""" | ||||
|                   _style "vertical-align:text-bottom;" | ||||
|                   _alt imgAlt | ||||
| @ -323,45 +324,44 @@ let print m version = | ||||
| 
 | ||||
| 
 | ||||
| /// View for the prayer request list | ||||
| let view m vi = | ||||
| let view model viewInfo = | ||||
|     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 "       " | ||||
|     let dtString  = m.Date.ToString "yyyy-MM-dd" | ||||
|     [   div [ _class "pt-center-text" ] [ | ||||
|             br [] | ||||
|             a [ _class  "pt-icon-link" | ||||
|                 _href   $"/prayer-requests/print/{dtString}" | ||||
|                 _target "_blank" | ||||
|                 _title  s["View Printable"].Value ] [ | ||||
|                 icon "print"; rawText "  "; locStr s["View Printable"] | ||||
|             ] | ||||
|             if m.CanEmail then | ||||
|                 spacer | ||||
|                 if m.Date.DayOfWeek <> DayOfWeek.Sunday then | ||||
|                     let rec findSunday (date : DateTime) = | ||||
|                         if date.DayOfWeek = DayOfWeek.Sunday then date else findSunday (date.AddDays 1.) | ||||
|                     let sunday = findSunday m.Date | ||||
|                     a [ _class "pt-icon-link" | ||||
|                         _href  $"""/prayer-requests/view/{sunday.ToString "yyyy-MM-dd"}""" | ||||
|                         _title s["List for Next Sunday"].Value ] [ | ||||
|                         icon "update"; rawText "  "; locStr s["List for Next Sunday"] | ||||
|                     ] | ||||
|                     spacer | ||||
|                 let emailPrompt = s["This will e-mail the current list to every member of your group, without further prompting.  Are you sure this is what you are ready to do?"].Value | ||||
|                 a [ _class   "pt-icon-link" | ||||
|                     _href    $"/prayer-requests/email/{dtString}" | ||||
|                     _title   s["Send via E-mail"].Value | ||||
|                     _onclick $"return PT.requests.view.promptBeforeEmail('{emailPrompt}')" ] [ | ||||
|                     icon "mail_outline"; rawText "  "; locStr s["Send via E-mail"] | ||||
|                 ] | ||||
|             spacer | ||||
|             a [ _class "pt-icon-link"; _href "/prayer-requests"; _title s["Maintain Prayer Requests"].Value ] [ | ||||
|                icon "compare_arrows"; rawText "  "; locStr s["Maintain Prayer Requests"] | ||||
|             ] | ||||
|         ] | ||||
|     let dtString  = model.Date.ToString "yyyy-MM-dd" | ||||
|     div [ _class "pt-center-text" ] [ | ||||
|         br [] | ||||
|         rawText (m.AsHtml s) | ||||
|         a [ _class  "pt-icon-link" | ||||
|             _href   $"/prayer-requests/print/{dtString}" | ||||
|             _target "_blank" | ||||
|             _title  s["View Printable"].Value ] [ | ||||
|             icon "print"; rawText "  "; locStr s["View Printable"] | ||||
|         ] | ||||
|         if model.CanEmail then | ||||
|             spacer | ||||
|             if model.Date.DayOfWeek <> DayOfWeek.Sunday then | ||||
|                 let rec findSunday (date : DateTime) = | ||||
|                     if date.DayOfWeek = DayOfWeek.Sunday then date else findSunday (date.AddDays 1.) | ||||
|                 let sunday = findSunday model.Date | ||||
|                 a [ _class "pt-icon-link" | ||||
|                     _href  $"""/prayer-requests/view/{sunday.ToString "yyyy-MM-dd"}""" | ||||
|                     _title s["List for Next Sunday"].Value ] [ | ||||
|                     icon "update"; rawText "  "; locStr s["List for Next Sunday"] | ||||
|                 ] | ||||
|                 spacer | ||||
|             let emailPrompt = s["This will e-mail the current list to every member of your group, without further prompting.  Are you sure this is what you are ready to do?"].Value | ||||
|             a [ _class   "pt-icon-link" | ||||
|                 _href    $"/prayer-requests/email/{dtString}" | ||||
|                 _title   s["Send via E-mail"].Value | ||||
|                 _onclick $"return PT.requests.view.promptBeforeEmail('{emailPrompt}')" ] [ | ||||
|                 icon "mail_outline"; rawText "  "; locStr s["Send via E-mail"] | ||||
|             ] | ||||
|         spacer | ||||
|         a [ _class "pt-icon-link"; _href "/prayer-requests"; _title s["Maintain Prayer Requests"].Value ] [ | ||||
|             icon "compare_arrows"; rawText "  "; locStr s["Maintain Prayer Requests"] | ||||
|         ] | ||||
|     ] | ||||
|     |> List.singleton | ||||
|     |> List.append [ br []; rawText (model.AsHtml s) ] | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard vi pageTitle | ||||
|     |> Layout.standard viewInfo pageTitle | ||||
|  | ||||
| @ -6,125 +6,125 @@ open PrayerTracker.Entities | ||||
| open PrayerTracker.ViewModels | ||||
| 
 | ||||
| /// View for the announcement page | ||||
| let announcement isAdmin ctx vi = | ||||
| let announcement isAdmin ctx viewInfo = | ||||
|     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 | ||||
|     [   form [ _action "/small-group/announcement/send"; _method "post"; _class "pt-center-columns"; Target.content ] [ | ||||
|             csrfToken ctx | ||||
|             div [ _class "pt-field-row" ] [ | ||||
|                 div [ _class "pt-field pt-editor" ] [ | ||||
|                     label [ _for "text" ] [ locStr s["Announcement Text"] ] | ||||
|                     textarea [ _name (nameof m.Text); _id "text"; _autofocus ] [] | ||||
|                 ] | ||||
|     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 | ||||
|         div [ _fieldRow ] [ | ||||
|             div [ _inputFieldWith [ "pt-editor" ] ] [ | ||||
|                 label [ _for "text" ] [ locStr s["Announcement Text"] ] | ||||
|                 textarea [ _name (nameof model.Text); _id "text"; _autofocus ] [] | ||||
|             ] | ||||
|             if isAdmin then | ||||
|                 div [ _class "pt-field-row" ] [ | ||||
|                     div [ _class "pt-field" ] [ | ||||
|                         label [] [ locStr s["Send Announcement to"]; rawText ":" ] | ||||
|                         div [ _class "pt-center-text" ] [ | ||||
|                             radio (nameof m.SendToClass) "sendY" "Y" "Y" | ||||
|                             label [ _for "sendY" ] [ locStr s["This Group"]; rawText "     " ] | ||||
|                             radio (nameof m.SendToClass) "sendN" "N" "Y" | ||||
|                             label [ _for "sendN" ] [ locStr s["All {0} Users", s["PrayerTracker"]] ] | ||||
|                         ] | ||||
|         ] | ||||
|         if isAdmin then | ||||
|             div [ _fieldRow ] [ | ||||
|                 div [ _inputField ] [ | ||||
|                     label [] [ locStr s["Send Announcement to"]; rawText ":" ] | ||||
|                     div [ _class "pt-center-text" ] [ | ||||
|                         radio (nameof model.SendToClass) "sendY" "Y" "Y" | ||||
|                         label [ _for "sendY" ] [ locStr s["This Group"]; rawText "     " ] | ||||
|                         radio (nameof model.SendToClass) "sendN" "N" "Y" | ||||
|                         label [ _for "sendN" ] [ locStr s["All {0} Users", s["PrayerTracker"]] ] | ||||
|                     ] | ||||
|                 ] | ||||
|             else input [ _type "hidden"; _name (nameof m.SendToClass); _value "Y" ] | ||||
|             div [ _class "pt-field-row pt-fadeable pt-shown"; _id "divAddToList" ] [ | ||||
|                 div [ _class "pt-checkbox-field" ] [ | ||||
|                     input [ _type "checkbox"; _name (nameof m.AddToRequestList); _id "addToRequestList"; _value "True" ] | ||||
|                     label [ _for "addToRequestList" ] [ locStr s["Add to Request List"] ] | ||||
|                 ] | ||||
|             ] | ||||
|             div [ _class "pt-field-row pt-fadeable"; _id "divCategory" ] [ | ||||
|                 div [ _class "pt-field" ] [ | ||||
|                     label [ _for (nameof m.RequestType) ] [ locStr s["Request Type"] ] | ||||
|                     reqTypes | ||||
|                     |> Seq.ofList | ||||
|                     |> Seq.map (fun (typ, desc) -> typ.code, desc.Value) | ||||
|                     |> selectList (nameof m.RequestType) Announcement.code [] | ||||
|                 ] | ||||
|         else input [ _type "hidden"; _name (nameof model.SendToClass); _value "Y" ] | ||||
|         div [ _fieldRowWith [ "pt-fadeable"; "pt-shown" ]; _id "divAddToList" ] [ | ||||
|             div [ _checkboxField ] [ | ||||
|                 input [ _type "checkbox"; _name (nameof model.AddToRequestList); _id "addToRequestList"; _value "True" ] | ||||
|                 label [ _for "addToRequestList" ] [ locStr s["Add to Request List"] ] | ||||
|             ] | ||||
|             div [ _class "pt-field-row" ] [ submit [] "send" s["Send Announcement"] ] | ||||
|         ] | ||||
|         script [] [ rawText "PT.onLoad(PT.smallGroup.announcement.onPageLoad)" ] | ||||
|         div [ _fieldRowWith [ "pt-fadeable" ]; _id "divCategory" ] [ | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for (nameof model.RequestType) ] [ locStr s["Request Type"] ] | ||||
|                 reqTypes | ||||
|                 |> Seq.ofList | ||||
|                 |> Seq.map (fun (typ, desc) -> typ.code, desc.Value) | ||||
|                 |> selectList (nameof model.RequestType) Announcement.code [] | ||||
|             ] | ||||
|         ] | ||||
|         div [ _fieldRow ] [ submit [] "send" s["Send Announcement"] ] | ||||
|     ] | ||||
|     |> List.singleton | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard vi "Send Announcement" | ||||
| 
 | ||||
| 
 | ||||
| /// View for once an announcement has been sent | ||||
| let announcementSent (m : Announcement) vi = | ||||
| let announcementSent (model : Announcement) viewInfo = | ||||
|     let s = I18N.localizer.Force () | ||||
|     [   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 [] | ||||
|         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.standard vi "Announcement Sent" | ||||
|     |> Layout.standard viewInfo "Announcement Sent" | ||||
| 
 | ||||
| 
 | ||||
| /// 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 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 ] [ | ||||
|         csrfToken ctx | ||||
|         input [ _type "hidden"; _name (nameof m.SmallGroupId); _value (flatGuid m.SmallGroupId) ] | ||||
|         div [ _class "pt-field-row" ] [ | ||||
|             div [ _class "pt-field" ] [ | ||||
|         input [ _type "hidden"; _name (nameof model.SmallGroupId); _value (flatGuid model.SmallGroupId) ] | ||||
|         div [ _fieldRow ] [ | ||||
|             div [ _inputField ] [ | ||||
|                 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 [ _class "pt-field" ] [ | ||||
|                 label [ _for (nameof m.ChurchId) ] [ locStr s["Church"] ] | ||||
|         div [ _fieldRow ] [ | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for (nameof model.ChurchId) ] [ locStr s["Church"] ] | ||||
|                 seq { | ||||
|                   "", selectDefault s["Select Church"].Value | ||||
|                   yield! churches |> List.map (fun c -> flatGuid c.churchId, c.name) | ||||
|                   } | ||||
|                 |> selectList (nameof m.ChurchId) (flatGuid m.ChurchId) [ _required ]  | ||||
|                     "", selectDefault s["Select Church"].Value | ||||
|                     yield! churches |> List.map (fun c -> flatGuid c.churchId, c.name) | ||||
|                 } | ||||
|                 |> 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 | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard vi pageTitle | ||||
|     |> Layout.standard viewInfo pageTitle | ||||
| 
 | ||||
| 
 | ||||
| /// 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 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 ] [ | ||||
|         style [ _scoped ] [ rawText "#name { width: 15rem; } #email { width: 20rem; }" ] | ||||
|         csrfToken ctx | ||||
|         input [ _type "hidden"; _name (nameof m.MemberId); _value (flatGuid m.MemberId) ] | ||||
|         div [ _class "pt-field-row" ] [ | ||||
|             div [ _class "pt-field" ] [ | ||||
|         input [ _type "hidden"; _name (nameof model.MemberId); _value (flatGuid model.MemberId) ] | ||||
|         div [ _fieldRow ] [ | ||||
|             div [ _inputField ] [ | ||||
|                 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"] ] | ||||
|                 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 [ _class "pt-field" ] [ | ||||
|                 label [ _for (nameof m.Format) ] [ locStr s["E-mail Format"] ] | ||||
|         div [ _fieldRow ] [ | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for (nameof model.Format) ] [ locStr s["E-mail Format"] ] | ||||
|                 types | ||||
|                 |> 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 | ||||
|     |> Layout.Content.standard | ||||
| @ -132,49 +132,49 @@ let editMember (m : EditMember) (types : (string * LocalizedString) seq) ctx vi | ||||
| 
 | ||||
| 
 | ||||
| /// View for the small group log on page | ||||
| let logOn (groups : SmallGroup list) grpId ctx vi = | ||||
|     let s = I18N.localizer.Force () | ||||
|     let m = { SmallGroupId = System.Guid.Empty; Password = ""; RememberMe = None } | ||||
|     [   form [ _action "/small-group/log-on/submit"; _method "post"; _class "pt-center-columns"; Target.body ] [ | ||||
|             csrfToken ctx | ||||
|             div [ _class "pt-field-row" ] [ | ||||
|                 div [ _class "pt-field" ] [ | ||||
|                     label [ _for (nameof m.SmallGroupId) ] [ locStr s["Group"] ] | ||||
|                     seq { | ||||
|                         if groups.Length = 0 then "", s["There are no classes with passwords defined"].Value | ||||
|                         else | ||||
|                             "", selectDefault s["Select Group"].Value | ||||
|                             yield! | ||||
|                                 groups | ||||
|                                 |> List.map (fun grp -> flatGuid grp.smallGroupId, $"{grp.church.name} | {grp.name}") | ||||
|                     } | ||||
|                     |> selectList (nameof m.SmallGroupId) grpId [ _required ] | ||||
|                 ] | ||||
|                 div [ _class "pt-field" ] [ | ||||
|                     label [ _for "password" ] [ locStr s["Password"] ] | ||||
|                     input [ _type        "password" | ||||
|                             _name        (nameof m.Password) | ||||
|                             _id          "password" | ||||
|                             _placeholder (s["Case-Sensitive"].Value.ToLower ()) | ||||
|                             _required ] | ||||
|                 ] | ||||
| let logOn (groups : SmallGroup list) grpId ctx viewInfo = | ||||
|     let s     = I18N.localizer.Force () | ||||
|     let model = { SmallGroupId = System.Guid.Empty; Password = ""; RememberMe = None } | ||||
|     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 | ||||
|         div [ _fieldRow ] [ | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for (nameof model.SmallGroupId) ] [ locStr s["Group"] ] | ||||
|                 seq { | ||||
|                     if groups.Length = 0 then "", s["There are no classes with passwords defined"].Value | ||||
|                     else | ||||
|                         "", selectDefault s["Select Group"].Value | ||||
|                         yield! | ||||
|                             groups | ||||
|                             |> List.map (fun grp -> flatGuid grp.smallGroupId, $"{grp.church.name} | {grp.name}") | ||||
|                 } | ||||
|                 |> selectList (nameof model.SmallGroupId) grpId [ _required ] | ||||
|             ] | ||||
|             div [ _class "pt-checkbox-field" ] [ | ||||
|                 input [ _type "checkbox"; _name (nameof m.RememberMe); _id "rememberMe"; _value "True" ] | ||||
|                 label [ _for "rememberMe" ] [ locStr s["Remember Me"] ] | ||||
|                 br [] | ||||
|                 small [] [ em [] [ str (s["Requires Cookies"].Value.ToLower ()) ] ] | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for "password" ] [ locStr s["Password"] ] | ||||
|                 input [ _type        "password" | ||||
|                         _name        (nameof model.Password) | ||||
|                         _id          "password" | ||||
|                         _placeholder (s["Case-Sensitive"].Value.ToLower ()) | ||||
|                         _required ] | ||||
|             ] | ||||
|             div [ _class "pt-field-row" ] [ submit [] "account_circle" s["Log On"] ] | ||||
|         ] | ||||
|         script [] [ rawText "PT.onLoad(PT.smallGroup.logOn.onPageLoad)" ] | ||||
|         div [ _checkboxField ] [ | ||||
|             input [ _type "checkbox"; _name (nameof model.RememberMe); _id "rememberMe"; _value "True" ] | ||||
|             label [ _for "rememberMe" ] [ locStr s["Remember Me"] ] | ||||
|             br [] | ||||
|             small [] [ em [] [ str (s["Requires Cookies"].Value.ToLower ()) ] ] | ||||
|         ] | ||||
|         div [ _fieldRow ] [ submit [] "account_circle" s["Log On"] ] | ||||
|     ] | ||||
|     |> List.singleton | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard vi "Group Log On" | ||||
| 
 | ||||
| 
 | ||||
| /// 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 grpTbl = | ||||
|         match groups with | ||||
| @ -223,11 +223,11 @@ let maintain (groups : SmallGroup list) ctx vi = | ||||
|         form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ] | ||||
|     ] | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard vi "Maintain Groups" | ||||
|     |> Layout.standard viewInfo "Maintain Groups" | ||||
| 
 | ||||
| 
 | ||||
| /// 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 mbrTbl = | ||||
|         match members with | ||||
| @ -262,7 +262,7 @@ let members (members : Member list) (emailTyps : Map<string, LocalizedString>) c | ||||
|                         ] | ||||
|                         td [] [ str mbr.memberName ] | ||||
|                         td [] [ str mbr.email ] | ||||
|                         td [] [ locStr emailTyps[defaultArg mbr.format ""] ] | ||||
|                         td [] [ locStr emailTypes[defaultArg mbr.format ""] ] | ||||
|                     ]) | ||||
|                 |> tbody [] | ||||
|             ] | ||||
| @ -279,13 +279,13 @@ let members (members : Member list) (emailTyps : Map<string, LocalizedString>) c | ||||
|         form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ] | ||||
|     ] | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard vi "Maintain Group Members" | ||||
|     |> Layout.standard viewInfo "Maintain Group Members" | ||||
| 
 | ||||
| 
 | ||||
| open Giraffe.ViewEngine.Accessibility | ||||
| 
 | ||||
| /// View for the small group overview page | ||||
| let overview m vi = | ||||
| let overview model viewInfo = | ||||
|     let s          = I18N.localizer.Force () | ||||
|     let linkSpacer = rawText "  " | ||||
|     let types      = ReferenceList.requestTypeList s |> dict | ||||
| @ -304,16 +304,16 @@ let overview m vi = | ||||
|             header [ _roleHeading ] [ iconSized 72 "question_answer"; locStr s["Prayer Requests"] ] | ||||
|             div [] [ | ||||
|                 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 [] | ||||
|                 for cat in m.ActiveReqsByType do | ||||
|                 for cat in model.ActiveReqsByType do | ||||
|                     str (cat.Value.ToString "N0") | ||||
|                     space | ||||
|                     locStr types[cat.Key] | ||||
|                     br [] | ||||
|                 br [] | ||||
|                 str (m.AllReqs.ToString "N0") | ||||
|                 str (model.AllReqs.ToString "N0") | ||||
|                 space | ||||
|                 locStr s["Total Requests"] | ||||
|                 hr [] | ||||
| @ -325,7 +325,7 @@ let overview m vi = | ||||
|         section [ _ariaLabel "Small group members" ] [ | ||||
|             header [ _roleHeading ] [ iconSized 72 "people_outline"; locStr s["Group Members"] ] | ||||
|             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 [] | ||||
|                 a [ _href "/small-group/members" ] [ icon "email"; linkSpacer; locStr s["Maintain Group Members"] ] | ||||
|             ] | ||||
| @ -333,236 +333,244 @@ let overview m vi = | ||||
|     ] | ||||
|     |> List.singleton | ||||
|     |> Layout.Content.wide | ||||
|     |> Layout.standard vi "Small Group Overview" | ||||
|     |> Layout.standard viewInfo "Small Group Overview" | ||||
| 
 | ||||
| 
 | ||||
| open System.IO | ||||
| open PrayerTracker | ||||
| 
 | ||||
| /// 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 l   = I18N.forView "SmallGroup/Preferences" | ||||
|     use sw  = new StringWriter () | ||||
|     let raw = rawLocText sw | ||||
|     [   style [ _scoped ] [ | ||||
|             rawText "#expireDays, #daysToKeepNew, #longTermUpdateWeeks, #headingFontSize, #listFontSize, #pageSize { width: 3rem; } #emailFromAddress { width: 20rem; } #fonts { width: 40rem; } @media screen and (max-width: 40rem) { #fonts { width: 100%; } }" | ||||
|     let vi  = | ||||
|         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%; } }" | ||||
|         ] | ||||
|         form [ _action "/small-group/preferences/save"; _method "post"; _class "pt-center-columns"; Target.content ] [ | ||||
|             csrfToken ctx | ||||
|             fieldset [] [ | ||||
|                 legend [] [ strong [] [ icon "date_range"; rawText "  "; locStr s["Dates"] ] ] | ||||
|                 div [ _class "pt-field-row" ] [ | ||||
|                     div [ _class "pt-field" ] [ | ||||
|                         label [ _for "expireDays" ] [ locStr s["Requests Expire After"] ] | ||||
|                         span [] [ | ||||
|                             input [ _type  "number" | ||||
|                                     _name  (nameof m.ExpireDays) | ||||
|                                     _id    "expireDays" | ||||
|                                     _value (string m.ExpireDays) | ||||
|                                     _min   "1"; _max "30" | ||||
|                                     _required | ||||
|                                     _autofocus ] | ||||
|                             space | ||||
|                             str (s["Days"].Value.ToLower ()) | ||||
|                         ] | ||||
|                     ] | ||||
|                     div [ _class "pt-field" ] [ | ||||
|                         label [ _for "daysToKeepNew" ] [ locStr s["Requests “New” For"] ] | ||||
|                         span [] [ | ||||
|                             input [ _type  "number" | ||||
|                                     _name  (nameof m.DaysToKeepNew) | ||||
|                                     _id    "daysToKeepNew" | ||||
|                                     _min   "1"; _max "30" | ||||
|                                     _value (string m.DaysToKeepNew) | ||||
|                                     _required ] | ||||
|                             space; str (s["Days"].Value.ToLower ()) | ||||
|                         ] | ||||
|                     ] | ||||
|                     div [ _class "pt-field" ] [ | ||||
|                         label [ _for "longTermUpdateWeeks" ] [ locStr s["Long-Term Requests Alerted for Update"] ] | ||||
|                         span [] [ | ||||
|                             input [ _type  "number" | ||||
|                                     _name  (nameof m.LongTermUpdateWeeks) | ||||
|                                     _id    "longTermUpdateWeeks" | ||||
|                                     _min   "1"; _max "30" | ||||
|                                     _value (string m.LongTermUpdateWeeks) | ||||
|                                     _required ] | ||||
|                             space; str (s["Weeks"].Value.ToLower ()) | ||||
|                         ] | ||||
|                     ] | ||||
|                 ] | ||||
|             ] | ||||
|             fieldset [] [ | ||||
|                 legend [] [ strong [] [ icon "sort"; rawText "  "; locStr s["Request Sorting"] ] ] | ||||
|                 radio (nameof m.RequestSort) "requestSort_D" "D" m.RequestSort | ||||
|                 label [ _for "requestSort_D" ] [ locStr s["Sort by Last Updated Date"] ] | ||||
|                 rawText "   " | ||||
|                 radio (nameof m.RequestSort) "requestSort_R" "R" m.RequestSort | ||||
|                 label [ _for "requestSort_R" ] [ locStr s["Sort by Requestor Name"] ] | ||||
|             ] | ||||
|             fieldset [] [ | ||||
|                 legend [] [ strong [] [ icon "mail_outline"; rawText "  "; locStr s["E-mail"] ] ] | ||||
|                 div [ _class "pt-field-row" ] [ | ||||
|                     div [ _class "pt-field" ] [ | ||||
|                         label [ _for "emailFromName" ] [ locStr s["From Name"] ] | ||||
|                         input [ _type  "text" | ||||
|                                 _name  (nameof m.EmailFromName) | ||||
|                                 _id    "emailFromName" | ||||
|                                 _value m.EmailFromName | ||||
|                                 _required ] | ||||
|                     ] | ||||
|                     div [ _class "pt-field" ] [ | ||||
|                         label [ _for "emailFromAddress" ] [ locStr s["From Address"] ] | ||||
|                         input [ _type  "email" | ||||
|                                 _name  (nameof m.EmailFromAddress) | ||||
|                                 _id    "emailFromAddress" | ||||
|                                 _value m.EmailFromAddress | ||||
|                                 _required ] | ||||
|                     ] | ||||
|                 ] | ||||
|                 div [ _class "pt-field-row" ] [ | ||||
|                     div [ _class "pt-field" ] [ | ||||
|                         label [ _for (nameof m.DefaultEmailType) ] [ locStr s["E-mail Format"] ] | ||||
|                         seq { | ||||
|                             "", selectDefault s["Select"].Value | ||||
|                             yield! | ||||
|                                 ReferenceList.emailTypeList HtmlFormat s | ||||
|                                 |> Seq.skip 1 | ||||
|                                 |> Seq.map (fun typ -> fst typ, (snd typ).Value) | ||||
|                         } | ||||
|                         |> selectList (nameof m.DefaultEmailType) m.DefaultEmailType [ _required ] | ||||
|                     ] | ||||
|                 ] | ||||
|             ] | ||||
|             fieldset [] [ | ||||
|                 legend [] [ strong [] [ icon "color_lens"; rawText "  "; locStr s["Colors"] ]; rawText " ***" ] | ||||
|                 div [ _class "pt-field-row" ] [ | ||||
|                     div [ _class "pt-field" ] [ | ||||
|                         label [ _class "pt-center-text" ] [ locStr s["Color of Heading Lines"] ] | ||||
|                         span [] [ | ||||
|                             radio (nameof m.LineColorType) "lineColorType_Name" "Name" m.LineColorType | ||||
|                             label [ _for "lineColorType_Name" ] [ locStr s["Named Color"] ] | ||||
|                             namedColorList (nameof m.LineColor) m.LineColor [ | ||||
|                                 _id "lineColor_Select" | ||||
|                                 if m.LineColor.StartsWith "#" then _disabled ] s | ||||
|                             rawText "    "; str (s["or"].Value.ToUpper ()) | ||||
|                             radio (nameof m.LineColorType) "lineColorType_RGB" "RGB" m.LineColorType | ||||
|                             label [ _for "lineColorType_RGB" ] [ locStr s["Custom Color"] ] | ||||
|                             input [ _type  "color"  | ||||
|                                     _name  (nameof m.LineColor) | ||||
|                                     _id    "lineColor_Color" | ||||
|                                     _value m.LineColor // TODO: convert to hex or skip if named | ||||
|                                     if not (m.LineColor.StartsWith "#") then _disabled ] | ||||
|                         ] | ||||
|                     ] | ||||
|                 ] | ||||
|                 div [ _class "pt-field-row" ] [ | ||||
|                     div [ _class "pt-field" ] [ | ||||
|                         label [ _class "pt-center-text" ] [ locStr s["Color of Heading Text"] ] | ||||
|                         span [] [ | ||||
|                             radio (nameof m.HeadingColorType) "headingColorType_Name" "Name" m.HeadingColorType | ||||
|                             label [ _for "headingColorType_Name" ] [ locStr s["Named Color"] ] | ||||
|                             namedColorList (nameof m.HeadingColor) m.HeadingColor [ | ||||
|                                 _id "headingColor_Select" | ||||
|                                 if m.HeadingColor.StartsWith "#" then _disabled ] s | ||||
|                             rawText "    "; str (s["or"].Value.ToUpper ()) | ||||
|                             radio (nameof m.HeadingColorType) "headingColorType_RGB" "RGB" m.HeadingColorType | ||||
|                             label [ _for "headingColorType_RGB" ] [ locStr s["Custom Color"] ] | ||||
|                             input [ _type  "color" | ||||
|                                     _name  (nameof m.HeadingColor) | ||||
|                                     _id    "headingColor_Color" | ||||
|                                     _value m.HeadingColor // TODO: convert to hex or skip if named | ||||
|                                     if not (m.HeadingColor.StartsWith "#") then _disabled ] | ||||
|                         ] | ||||
|                     ] | ||||
|                 ] | ||||
|             ] | ||||
|             fieldset [] [ | ||||
|                 legend [] [ strong [] [ icon "font_download"; rawText "  "; locStr s["Fonts"] ] ] | ||||
|                 div [ _class "pt-field" ] [ | ||||
|                     label [ _for "fonts" ] [ locStr s["Fonts** for List"] ] | ||||
|                     input [ _type "text"; _name (nameof m.Fonts); _id "fonts"; _required; _value m.Fonts ] | ||||
|                 ] | ||||
|                 div [ _class "pt-field-row" ] [ | ||||
|                     div [ _class "pt-field" ] [ | ||||
|                         label [ _for "headingFontSize" ] [ locStr s["Heading Text Size"] ] | ||||
|                         input [ _type  "number" | ||||
|                                 _name  (nameof m.HeadingFontSize) | ||||
|                                 _id    "headingFontSize" | ||||
|                                 _min   "8"; _max "24" | ||||
|                                 _value (string m.HeadingFontSize) | ||||
|                                 _required ] | ||||
|                     ] | ||||
|                     div [ _class "pt-field" ] [ | ||||
|                         label [ _for "listFontSize" ] [ locStr s["List Text Size"] ] | ||||
|                         input [ _type  "number" | ||||
|                                 _name  (nameof m.ListFontSize) | ||||
|                                 _id    "listFontSize" | ||||
|                                 _min   "8"; _max "24" | ||||
|                                 _value (string m.ListFontSize) | ||||
|                                 _required ] | ||||
|                     ] | ||||
|                 ] | ||||
|             ] | ||||
|             fieldset [] [ | ||||
|                 legend [] [ strong [] [ icon "settings"; rawText "  "; locStr s["Other Settings"] ] ] | ||||
|                 div [ _class "pt-field-row" ] [ | ||||
|                     div [ _class "pt-field" ] [ | ||||
|                         label [ _for (nameof m.TimeZone) ] [ locStr s["Time Zone"] ] | ||||
|                         seq { | ||||
|                             "", selectDefault s["Select"].Value | ||||
|                             yield! tzs |> List.map (fun tz -> tz.timeZoneId, (TimeZones.name tz.timeZoneId s).Value) | ||||
|                         } | ||||
|                         |> selectList (nameof m.TimeZone) m.TimeZone [ _required ] | ||||
|                     ] | ||||
|                 ] | ||||
|                 div [ _class "pt-field" ] [ | ||||
|                     label [] [ locStr s["Request List Visibility"] ] | ||||
|         |> AppViewInfo.withOnLoadScript "PT.smallGroup.preferences.onPageLoad" | ||||
|     form [ _action "/small-group/preferences/save"; _method "post"; _class "pt-center-columns"; Target.content ] [ | ||||
|         csrfToken ctx | ||||
|         fieldset [] [ | ||||
|             legend [] [ strong [] [ icon "date_range"; rawText "  "; locStr s["Dates"] ] ] | ||||
|             div [ _fieldRow ] [ | ||||
|                 div [ _inputField ] [ | ||||
|                     label [ _for "expireDays" ] [ locStr s["Requests Expire After"] ] | ||||
|                     span [] [ | ||||
|                         radio (nameof m.Visibility) "viz_Public" (string RequestVisibility.``public``) | ||||
|                               (string m.Visibility) | ||||
|                         label [ _for "viz_Public" ] [ locStr s["Public"] ] | ||||
|                         rawText "  " | ||||
|                         radio (nameof m.Visibility) "viz_Private" (string RequestVisibility.``private``) | ||||
|                               (string m.Visibility) | ||||
|                         label [ _for "viz_Private" ] [ locStr s["Private"] ] | ||||
|                         rawText "  " | ||||
|                         radio (nameof m.Visibility) "viz_Password" (string RequestVisibility.passwordProtected) | ||||
|                               (string m.Visibility) | ||||
|                         label [ _for "viz_Password" ] [ locStr s["Password Protected"] ] | ||||
|                     ] | ||||
|                 ] | ||||
|                 let classSuffix = if m.Visibility = RequestVisibility.passwordProtected then " pt-show" else "" | ||||
|                 div [ _id "divClassPassword"; _class $"pt-field-row pt-fadeable{classSuffix}" ] [ | ||||
|                     div [ _class "pt-field" ] [ | ||||
|                         label [ _for "groupPassword" ] [ locStr s["Group Password (Used to Read Online)"] ] | ||||
|                         input [ _type  "text" | ||||
|                                 _name  (nameof m.GroupPassword) | ||||
|                                 _id    "groupPassword" | ||||
|                                 _value (defaultArg m.GroupPassword "") ] | ||||
|                     ] | ||||
|                 ] | ||||
|                 div [ _class "pt-field-row" ] [ | ||||
|                     div [ _class "pt-field" ] [ | ||||
|                         label [ _for "pageSize" ] [ locStr s["Page Size"] ] | ||||
|                         input [ _type  "number" | ||||
|                                 _name  (nameof m.PageSize) | ||||
|                                 _id    "pageSize" | ||||
|                                 _min   "10"; _max "255" | ||||
|                                 _value (string m.PageSize) | ||||
|                                 _required ] | ||||
|                                 _name  (nameof model.ExpireDays) | ||||
|                                 _id    "expireDays" | ||||
|                                 _value (string model.ExpireDays) | ||||
|                                 _min   "1"; _max "30" | ||||
|                                 _required | ||||
|                                 _autofocus ] | ||||
|                         space | ||||
|                         str (s["Days"].Value.ToLower ()) | ||||
|                     ] | ||||
|                     div [ _class "pt-field" ] [ | ||||
|                         label [ _for (nameof m.AsOfDate) ] [ locStr s["“As of” Date Display"] ] | ||||
|                         ReferenceList.asOfDateList s | ||||
|                         |> List.map (fun (code, desc) -> code, desc.Value) | ||||
|                         |> selectList (nameof m.AsOfDate) m.AsOfDate [ _required ] | ||||
|                 ] | ||||
|                 div [ _inputField ] [ | ||||
|                     label [ _for "daysToKeepNew" ] [ locStr s["Requests “New” For"] ] | ||||
|                     span [] [ | ||||
|                         input [ _type  "number" | ||||
|                                 _name  (nameof model.DaysToKeepNew) | ||||
|                                 _id    "daysToKeepNew" | ||||
|                                 _min   "1"; _max "30" | ||||
|                                 _value (string model.DaysToKeepNew) | ||||
|                                 _required ] | ||||
|                         space; str (s["Days"].Value.ToLower ()) | ||||
|                     ] | ||||
|                 ] | ||||
|                 div [ _inputField ] [ | ||||
|                     label [ _for "longTermUpdateWeeks" ] [ locStr s["Long-Term Requests Alerted for Update"] ] | ||||
|                     span [] [ | ||||
|                         input [ _type  "number" | ||||
|                                 _name  (nameof model.LongTermUpdateWeeks) | ||||
|                                 _id    "longTermUpdateWeeks" | ||||
|                                 _min   "1"; _max "30" | ||||
|                                 _value (string model.LongTermUpdateWeeks) | ||||
|                                 _required ] | ||||
|                         space; str (s["Weeks"].Value.ToLower ()) | ||||
|                     ] | ||||
|                 ] | ||||
|             ] | ||||
|             div [ _class "pt-field-row" ] [ submit [] "save" s["Save Preferences"] ] | ||||
|         ] | ||||
|         fieldset [] [ | ||||
|             legend [] [ strong [] [ icon "sort"; rawText "  "; locStr s["Request Sorting"] ] ] | ||||
|             radio (nameof model.RequestSort) "requestSort_D" "D" model.RequestSort | ||||
|             label [ _for "requestSort_D" ] [ locStr s["Sort by Last Updated Date"] ] | ||||
|             rawText "   " | ||||
|             radio (nameof model.RequestSort) "requestSort_R" "R" model.RequestSort | ||||
|             label [ _for "requestSort_R" ] [ locStr s["Sort by Requestor Name"] ] | ||||
|         ] | ||||
|         fieldset [] [ | ||||
|             legend [] [ strong [] [ icon "mail_outline"; rawText "  "; locStr s["E-mail"] ] ] | ||||
|             div [ _fieldRow ] [ | ||||
|                 div [ _inputField ] [ | ||||
|                     label [ _for "emailFromName" ] [ locStr s["From Name"] ] | ||||
|                     input [ _type  "text" | ||||
|                             _name  (nameof model.EmailFromName) | ||||
|                             _id    "emailFromName" | ||||
|                             _value model.EmailFromName | ||||
|                             _required ] | ||||
|                 ] | ||||
|                 div [ _inputField ] [ | ||||
|                     label [ _for "emailFromAddress" ] [ locStr s["From Address"] ] | ||||
|                     input [ _type  "email" | ||||
|                             _name  (nameof model.EmailFromAddress) | ||||
|                             _id    "emailFromAddress" | ||||
|                             _value model.EmailFromAddress | ||||
|                             _required ] | ||||
|                 ] | ||||
|             ] | ||||
|             div [ _fieldRow ] [ | ||||
|                 div [ _inputField ] [ | ||||
|                     label [ _for (nameof model.DefaultEmailType) ] [ locStr s["E-mail Format"] ] | ||||
|                     seq { | ||||
|                         "", selectDefault s["Select"].Value | ||||
|                         yield! | ||||
|                             ReferenceList.emailTypeList HtmlFormat s | ||||
|                             |> Seq.skip 1 | ||||
|                             |> Seq.map (fun typ -> fst typ, (snd typ).Value) | ||||
|                     } | ||||
|                     |> selectList (nameof model.DefaultEmailType) model.DefaultEmailType [ _required ] | ||||
|                 ] | ||||
|             ] | ||||
|         ] | ||||
|         fieldset [] [ | ||||
|             legend [] [ strong [] [ icon "color_lens"; rawText "  "; locStr s["Colors"] ]; rawText " ***" ] | ||||
|             div [ _fieldRow ] [ | ||||
|                 div [ _inputField ] [ | ||||
|                     label [ _class "pt-center-text" ] [ locStr s["Color of Heading Lines"] ] | ||||
|                     span [] [ | ||||
|                         radio (nameof model.LineColorType) "lineColorType_Name" "Name" model.LineColorType | ||||
|                         label [ _for "lineColorType_Name" ] [ locStr s["Named Color"] ] | ||||
|                         namedColorList (nameof model.LineColor) model.LineColor [ | ||||
|                             _id "lineColor_Select" | ||||
|                             if model.LineColor.StartsWith "#" then _disabled ] s | ||||
|                         rawText "    "; str (s["or"].Value.ToUpper ()) | ||||
|                         radio (nameof model.LineColorType) "lineColorType_RGB" "RGB" model.LineColorType | ||||
|                         label [ _for "lineColorType_RGB" ] [ locStr s["Custom Color"] ] | ||||
|                         input [ _type  "color"  | ||||
|                                 _name  (nameof model.LineColor) | ||||
|                                 _id    "lineColor_Color" | ||||
|                                 _value model.LineColor // TODO: convert to hex or skip if named | ||||
|                                 if not (model.LineColor.StartsWith "#") then _disabled ] | ||||
|                     ] | ||||
|                 ] | ||||
|             ] | ||||
|             div [ _fieldRow ] [ | ||||
|                 div [ _inputField ] [ | ||||
|                     label [ _class "pt-center-text" ] [ locStr s["Color of Heading Text"] ] | ||||
|                     span [] [ | ||||
|                         radio (nameof model.HeadingColorType) "headingColorType_Name" "Name" model.HeadingColorType | ||||
|                         label [ _for "headingColorType_Name" ] [ locStr s["Named Color"] ] | ||||
|                         namedColorList (nameof model.HeadingColor) model.HeadingColor [ | ||||
|                             _id "headingColor_Select" | ||||
|                             if model.HeadingColor.StartsWith "#" then _disabled ] s | ||||
|                         rawText "    "; str (s["or"].Value.ToUpper ()) | ||||
|                         radio (nameof model.HeadingColorType) "headingColorType_RGB" "RGB" model.HeadingColorType | ||||
|                         label [ _for "headingColorType_RGB" ] [ locStr s["Custom Color"] ] | ||||
|                         input [ _type  "color" | ||||
|                                 _name  (nameof model.HeadingColor) | ||||
|                                 _id    "headingColor_Color" | ||||
|                                 _value model.HeadingColor // TODO: convert to hex or skip if named | ||||
|                                 if not (model.HeadingColor.StartsWith "#") then _disabled ] | ||||
|                     ] | ||||
|                 ] | ||||
|             ] | ||||
|         ] | ||||
|         fieldset [] [ | ||||
|             legend [] [ strong [] [ icon "font_download"; rawText "  "; locStr s["Fonts"] ] ] | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for "fonts" ] [ locStr s["Fonts** for List"] ] | ||||
|                 input [ _type "text"; _name (nameof model.Fonts); _id "fonts"; _required; _value model.Fonts ] | ||||
|             ] | ||||
|             div [ _fieldRow ] [ | ||||
|                 div [ _inputField ] [ | ||||
|                     label [ _for "headingFontSize" ] [ locStr s["Heading Text Size"] ] | ||||
|                     input [ _type  "number" | ||||
|                             _name  (nameof model.HeadingFontSize) | ||||
|                             _id    "headingFontSize" | ||||
|                             _min   "8"; _max "24" | ||||
|                             _value (string model.HeadingFontSize) | ||||
|                             _required ] | ||||
|                 ] | ||||
|                 div [ _inputField ] [ | ||||
|                     label [ _for "listFontSize" ] [ locStr s["List Text Size"] ] | ||||
|                     input [ _type  "number" | ||||
|                             _name  (nameof model.ListFontSize) | ||||
|                             _id    "listFontSize" | ||||
|                             _min   "8"; _max "24" | ||||
|                             _value (string model.ListFontSize) | ||||
|                             _required ] | ||||
|                 ] | ||||
|             ] | ||||
|         ] | ||||
|         fieldset [] [ | ||||
|             legend [] [ strong [] [ icon "settings"; rawText "  "; locStr s["Other Settings"] ] ] | ||||
|             div [ _fieldRow ] [ | ||||
|                 div [ _inputField ] [ | ||||
|                     label [ _for (nameof model.TimeZone) ] [ locStr s["Time Zone"] ] | ||||
|                     seq { | ||||
|                         "", selectDefault s["Select"].Value | ||||
|                         yield! tzs |> List.map (fun tz -> tz.timeZoneId, (TimeZones.name tz.timeZoneId s).Value) | ||||
|                     } | ||||
|                     |> selectList (nameof model.TimeZone) model.TimeZone [ _required ] | ||||
|                 ] | ||||
|             ] | ||||
|             div [ _inputField ] [ | ||||
|                 label [] [ locStr s["Request List Visibility"] ] | ||||
|                 span [] [ | ||||
|                     radio (nameof model.Visibility) "viz_Public" (string RequestVisibility.``public``) | ||||
|                           (string model.Visibility) | ||||
|                     label [ _for "viz_Public" ] [ locStr s["Public"] ] | ||||
|                     rawText "  " | ||||
|                     radio (nameof model.Visibility) "viz_Private" (string RequestVisibility.``private``) | ||||
|                           (string model.Visibility) | ||||
|                     label [ _for "viz_Private" ] [ locStr s["Private"] ] | ||||
|                     rawText "  " | ||||
|                     radio (nameof model.Visibility) "viz_Password" (string RequestVisibility.passwordProtected) | ||||
|                           (string model.Visibility) | ||||
|                     label [ _for "viz_Password" ] [ locStr s["Password Protected"] ] | ||||
|                 ] | ||||
|             ] | ||||
|             let classSuffix = if model.Visibility = RequestVisibility.passwordProtected then [ "pt-show" ] else [] | ||||
|             div [ _id "divClassPassword"; _fieldRowWith ("pt-fadeable" :: classSuffix) ] [ | ||||
|                 div [ _inputField ] [ | ||||
|                     label [ _for "groupPassword" ] [ locStr s["Group Password (Used to Read Online)"] ] | ||||
|                     input [ _type  "text" | ||||
|                             _name  (nameof model.GroupPassword) | ||||
|                             _id    "groupPassword" | ||||
|                             _value (defaultArg model.GroupPassword "") ] | ||||
|                 ] | ||||
|             ] | ||||
|             div [ _fieldRow ] [ | ||||
|                 div [ _inputField ] [ | ||||
|                     label [ _for "pageSize" ] [ locStr s["Page Size"] ] | ||||
|                     input [ _type  "number" | ||||
|                             _name  (nameof model.PageSize) | ||||
|                             _id    "pageSize" | ||||
|                             _min   "10"; _max "255" | ||||
|                             _value (string model.PageSize) | ||||
|                             _required ] | ||||
|                 ] | ||||
|                 div [ _inputField ] [ | ||||
|                     label [ _for (nameof model.AsOfDate) ] [ locStr s["“As of” Date Display"] ] | ||||
|                     ReferenceList.asOfDateList s | ||||
|                     |> List.map (fun (code, desc) -> code, desc.Value) | ||||
|                     |> selectList (nameof model.AsOfDate) model.AsOfDate [ _required ] | ||||
|                 ] | ||||
|             ] | ||||
|         ] | ||||
|         div [ _fieldRow ] [ submit [] "save" s["Save Preferences"] ] | ||||
|     ] | ||||
|     |> List.singleton | ||||
|     |> List.append [ | ||||
|         p [] [ | ||||
|             rawText "** " | ||||
|             raw l["List font names, separated by commas."] | ||||
| @ -575,7 +583,6 @@ let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi = | ||||
|             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>."] | ||||
|         ] | ||||
|         script [] [ rawText "PT.onLoad(PT.smallGroup.preferences.onPageLoad)" ] | ||||
|     ] | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard vi "Group Preferences" | ||||
|  | ||||
| @ -4,13 +4,13 @@ open Giraffe.ViewEngine | ||||
| open PrayerTracker.ViewModels | ||||
| 
 | ||||
| /// 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 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 ] [ | ||||
|         csrfToken ctx | ||||
|         input [ _type "hidden"; _name (nameof m.UserId); _value (flatGuid m.UserId) ] | ||||
|         input [ _type "hidden"; _name (nameof m.UserName); _value m.UserName ] | ||||
|         input [ _type "hidden"; _name (nameof model.UserId); _value (flatGuid model.UserId) ] | ||||
|         input [ _type "hidden"; _name (nameof model.UserName); _value model.UserName ] | ||||
|         table [ _class "pt-table" ] [ | ||||
|             thead [] [ | ||||
|                 tr [] [ | ||||
| @ -24,7 +24,7 @@ let assignGroups m groups curGroups ctx vi = | ||||
|                 tr [] [ | ||||
|                     td [] [ | ||||
|                         input [ _type  "checkbox" | ||||
|                                 _name  (nameof m.SmallGroups) | ||||
|                                 _name  (nameof model.SmallGroups) | ||||
|                                 _id    inputId | ||||
|                                 _value grpId | ||||
|                                 if List.contains grpId curGroups then _checked ] | ||||
| @ -33,43 +33,53 @@ let assignGroups m groups curGroups ctx vi = | ||||
|                 ]) | ||||
|             |> tbody [] | ||||
|         ] | ||||
|         div [ _class "pt-field-row" ] [ submit [] "save" s["Save Group Assignments"] ] | ||||
|         div [ _fieldRow ] [ submit [] "save" s["Save Group Assignments"] ] | ||||
|     ] | ||||
|     |> List.singleton | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard vi pageTitle | ||||
|     |> Layout.standard viewInfo pageTitle | ||||
| 
 | ||||
| 
 | ||||
| /// View for the password change page | ||||
| let changePassword ctx vi = | ||||
| let changePassword ctx viewInfo = | ||||
|     let s = I18N.localizer.Force () | ||||
|     let m = { OldPassword = ""; NewPassword = ""; NewPasswordConfirm = "" } | ||||
|     [   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."] | ||||
|         ] | ||||
|         style [ _scoped ] [ rawText "#oldPassword, #newPassword, #newPasswordConfirm { width: 10rem; } "] | ||||
|     let model = { OldPassword = ""; NewPassword = ""; NewPasswordConfirm = "" } | ||||
|     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."] | ||||
|     ] | ||||
|     |> List.singleton | ||||
|     |> List.append [ | ||||
|         form [ _action   "/user/password/change" | ||||
|                _method   "post" | ||||
|                _onsubmit $"""return PT.compareValidation('newPassword','newPasswordConfirm','%A{s["The passwords do not match"]}')""" | ||||
|                Target.content ] [ | ||||
|             csrfToken ctx | ||||
|             div [ _class "pt-field-row" ] [ | ||||
|                 div [ _class "pt-field" ] [ | ||||
|             div [ _fieldRow ] [ | ||||
|                 div [ _inputField ] [ | ||||
|                     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 [ _class "pt-field" ] [ | ||||
|             div [ _fieldRow ] [ | ||||
|                 div [ _inputField ] [ | ||||
|                     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 " " ] | ||||
|                     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" | ||||
|                        s["Change Your Password"] | ||||
|             ] | ||||
| @ -80,102 +90,110 @@ let changePassword ctx vi = | ||||
| 
 | ||||
| 
 | ||||
| /// View for the edit user page | ||||
| let edit (m : EditUser) ctx vi = | ||||
| let edit (model : EditUser) ctx viewInfo = | ||||
|     let s             = I18N.localizer.Force () | ||||
|     let pageTitle     = if m.IsNew then "Add a New User" else "Edit User" | ||||
|     let pwPlaceholder = s[if m.IsNew then "" else "No change"].Value | ||||
|     [   style [ _scoped ] | ||||
|               [ rawText "#firstName, #lastName, #password, #passwordConfirm { width: 10rem; } #email { width: 20rem; } " ] | ||||
|         form [ _action   "/user/edit/save" | ||||
|                _method   "post" | ||||
|                _class    "pt-center-columns" | ||||
|                _onsubmit $"""return PT.compareValidation('password','passwordConfirm','%A{s["The passwords do not match"]}')""" | ||||
|                Target.content ] [ | ||||
|             csrfToken ctx | ||||
|             input [ _type "hidden"; _name (nameof m.UserId); _value (flatGuid m.UserId) ] | ||||
|             div [ _class "pt-field-row" ] [ | ||||
|                 div [ _class "pt-field" ] [ | ||||
|                     label [ _for "firstName" ] [ locStr s["First Name"] ] | ||||
|                     input [ _type  "text" | ||||
|                             _name  (nameof m.FirstName) | ||||
|                             _id    "firstName" | ||||
|                             _value m.FirstName | ||||
|                             _required | ||||
|                             _autofocus ] | ||||
|                 ] | ||||
|                 div [ _class "pt-field" ] [ | ||||
|                     label [ _for "lastName" ] [ locStr s["Last Name"] ] | ||||
|                     input [ _type "text"; _name (nameof m.LastName); _id "lastName"; _value m.LastName; _required ] | ||||
|                 ] | ||||
|                 div [ _class "pt-field" ] [ | ||||
|                     label [ _for "email" ] [ locStr s["E-mail Address"] ] | ||||
|                     input [ _type "email"; _name (nameof m.Email); _id "email"; _value m.Email; _required ] | ||||
|                 ] | ||||
|             ] | ||||
|             div [ _class "pt-field-row" ] [ | ||||
|                 div [ _class "pt-field" ] [ | ||||
|                     label [ _for "password" ] [ locStr s["Password"] ] | ||||
|                     input [ _type "password"; _name (nameof m.Password); _id "password"; _placeholder pwPlaceholder ] | ||||
|                 ] | ||||
|                 div [ _class "pt-field" ] [ | ||||
|                     label [ _for "passwordConfirm" ] [ locStr s["Password Again"] ] | ||||
|                     input [ _type        "password" | ||||
|                             _name        (nameof m.PasswordConfirm) | ||||
|                             _id          "passwordConfirm" | ||||
|                             _placeholder pwPlaceholder ] | ||||
|                 ] | ||||
|             ] | ||||
|             div [ _class "pt-checkbox-field" ] [ | ||||
|                 input [ _type  "checkbox" | ||||
|                         _name  (nameof m.IsAdmin) | ||||
|                         _id    "isAdmin" | ||||
|                         _value "True" | ||||
|                         if defaultArg m.IsAdmin false then _checked ] | ||||
|                 label [ _for "isAdmin" ] [ locStr s["This user is a PrayerTracker administrator"] ] | ||||
|             ] | ||||
|             div [ _class "pt-field-row" ] [ submit [] "save" s["Save User"] ] | ||||
|     let pageTitle     = if model.IsNew then "Add a New User" else "Edit User" | ||||
|     let pwPlaceholder = s[if model.IsNew then "" else "No change"].Value | ||||
|     let vi = | ||||
|         viewInfo | ||||
|         |> AppViewInfo.withScopedStyles [ | ||||
|             "#firstName, #lastName, #password, #passwordConfirm { width: 10rem; }" | ||||
|             "#email { width: 20rem; }" | ||||
|         ] | ||||
|         script [] [ rawText $"PT.onLoad(PT.user.edit.onPageLoad({(string m.IsNew).ToLowerInvariant ()}))" ] | ||||
|         |> AppViewInfo.withOnLoadScript $"PT.user.edit.onPageLoad({(string model.IsNew).ToLowerInvariant ()})" | ||||
|     form [ _action   "/user/edit/save" | ||||
|            _method   "post" | ||||
|            _class    "pt-center-columns" | ||||
|            _onsubmit $"""return PT.compareValidation('password','passwordConfirm','%A{s["The passwords do not match"]}')""" | ||||
|            Target.content ] [ | ||||
|         csrfToken ctx | ||||
|         input [ _type "hidden"; _name (nameof model.UserId); _value (flatGuid model.UserId) ] | ||||
|         div [ _fieldRow ] [ | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for "firstName" ] [ locStr s["First Name"] ] | ||||
|                 input [ _type  "text" | ||||
|                         _name  (nameof model.FirstName) | ||||
|                         _id    "firstName" | ||||
|                         _value model.FirstName | ||||
|                         _required | ||||
|                         _autofocus ] | ||||
|             ] | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for "lastName" ] [ locStr s["Last Name"] ] | ||||
|                 input [ _type "text"; _name (nameof model.LastName); _id "lastName"; _value model.LastName; _required ] | ||||
|             ] | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for "email" ] [ locStr s["E-mail Address"] ] | ||||
|                 input [ _type "email"; _name (nameof model.Email); _id "email"; _value model.Email; _required ] | ||||
|             ] | ||||
|         ] | ||||
|         div [ _fieldRow ] [ | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for "password" ] [ locStr s["Password"] ] | ||||
|                 input [ _type "password"; _name (nameof model.Password); _id "password"; _placeholder pwPlaceholder ] | ||||
|             ] | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for "passwordConfirm" ] [ locStr s["Password Again"] ] | ||||
|                 input [ _type        "password" | ||||
|                         _name        (nameof model.PasswordConfirm) | ||||
|                         _id          "passwordConfirm" | ||||
|                         _placeholder pwPlaceholder ] | ||||
|             ] | ||||
|         ] | ||||
|         div [ _checkboxField ] [ | ||||
|             input [ _type  "checkbox" | ||||
|                     _name  (nameof model.IsAdmin) | ||||
|                     _id    "isAdmin" | ||||
|                     _value "True" | ||||
|                     if defaultArg model.IsAdmin false then _checked ] | ||||
|             label [ _for "isAdmin" ] [ locStr s["This user is a PrayerTracker administrator"] ] | ||||
|         ] | ||||
|         div [ _fieldRow ] [ submit [] "save" s["Save User"] ] | ||||
|     ] | ||||
|     |> List.singleton | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard vi pageTitle | ||||
| 
 | ||||
| 
 | ||||
| /// View for the user log on page | ||||
| let logOn (m : UserLogOn) groups ctx vi = | ||||
|     let s = I18N.localizer.Force () | ||||
| let logOn (model : UserLogOn) groups ctx viewInfo = | ||||
|     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 ] [ | ||||
|         style [ _scoped ] [ rawText "#email { width: 20rem; }" ] | ||||
|         csrfToken ctx | ||||
|         input [ _type "hidden"; _name (nameof m.RedirectUrl); _value (defaultArg m.RedirectUrl "") ] | ||||
|         div [ _class "pt-field-row" ] [ | ||||
|             div [ _class "pt-field" ] [ | ||||
|         input [ _type "hidden"; _name (nameof model.RedirectUrl); _value (defaultArg model.RedirectUrl "") ] | ||||
|         div [ _fieldRow ] [ | ||||
|             div [ _inputField ] [ | ||||
|                 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"] ] | ||||
|                 input [ _type        "password" | ||||
|                         _name        (nameof m.Password) | ||||
|                         _name        (nameof model.Password) | ||||
|                         _id          "password" | ||||
|                         _placeholder $"""({s["Case-Sensitive"].Value.ToLower ()})""" | ||||
|                         _required ] | ||||
|             ] | ||||
|         ] | ||||
|         div [ _class "pt-field-row" ] [ | ||||
|             div [ _class "pt-field" ] [ | ||||
|                 label [ _for (nameof m.SmallGroupId) ] [ locStr s["Group"] ] | ||||
|         div [ _fieldRow ] [ | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for (nameof model.SmallGroupId) ] [ locStr s["Group"] ] | ||||
|                 seq { "", selectDefault s["Select Group"].Value; yield! groups } | ||||
|                 |> selectList (nameof m.SmallGroupId) "" [ _required ] | ||||
|                 |> selectList (nameof model.SmallGroupId) "" [ _required ] | ||||
|             ] | ||||
|         ] | ||||
|         div [ _class "pt-checkbox-field" ] [ | ||||
|             input [ _type "checkbox"; _name (nameof m.RememberMe); _id "rememberMe"; _value "True" ] | ||||
|         div [ _checkboxField ] [ | ||||
|             input [ _type "checkbox"; _name (nameof model.RememberMe); _id "rememberMe"; _value "True" ] | ||||
|             label [ _for "rememberMe" ] [ locStr s["Remember Me"] ] | ||||
|             br [] | ||||
|             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 | ||||
|     |> Layout.Content.standard | ||||
| @ -185,7 +203,7 @@ let logOn (m : UserLogOn) groups ctx vi = | ||||
| open PrayerTracker.Entities | ||||
| 
 | ||||
| /// 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 usrTbl = | ||||
|         match users with | ||||
| @ -237,4 +255,4 @@ let maintain (users : User list) ctx vi = | ||||
|         form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ] | ||||
|     ] | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard vi "Maintain Users" | ||||
|     |> Layout.standard viewInfo "Maintain Users" | ||||
|  | ||||
| @ -147,23 +147,40 @@ type AppViewInfo = | ||||
|          | ||||
|         /// The layout with which the content will be rendered | ||||
|         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 | ||||
| module AppViewInfo = | ||||
|      | ||||
|     /// A fresh version that can be populated to process the current request | ||||
|     let fresh = | ||||
|         { Style        = [] | ||||
|           Script       = [] | ||||
|           HelpLink     = None | ||||
|           Messages     = [] | ||||
|           Version      = "" | ||||
|           RequestStart = DateTime.Now.Ticks | ||||
|           User         = None | ||||
|           Group        = None | ||||
|           Layout       = FullPage | ||||
|         {   Style        = [] | ||||
|             Script       = [] | ||||
|             HelpLink     = None | ||||
|             Messages     = [] | ||||
|             Version      = "" | ||||
|             RequestStart = DateTime.Now.Ticks | ||||
|             User         = None | ||||
|             Group        = None | ||||
|             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 | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
|  * This file contains a library of common functions used throughout the PrayerTracker website, as well as specific | ||||
|  * functions used on pages throughout the site. | ||||
|  */ | ||||
| const PT = { | ||||
| this.PT = { | ||||
| 
 | ||||
|   /** | ||||
|    * Open a window with help | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user