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,24 +147,41 @@ 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
|
||||
[<CLIMutable; NoComparison; NoEquality>]
|
||||
|
|
|
@ -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…
Reference in New Issue
Block a user