Initial htmx nav works (#36)
- Use nameof for field IDs - Add helper functions to streamline forms
This commit is contained in:
parent
f66dd76dfb
commit
7786896dfd
|
@ -11,10 +11,10 @@ let edit (model : EditChurch) ctx viewInfo =
|
|||
let vi =
|
||||
viewInfo
|
||||
|> AppViewInfo.withScopedStyles [
|
||||
"#name { width: 20rem; }"
|
||||
"#city { width: 10rem; }"
|
||||
"#st { width: 3rem; }"
|
||||
"#interfaceAddress { width: 30rem; }"
|
||||
$"#{nameof model.Name} {{ width: 20rem; }}"
|
||||
$"#{nameof model.City} {{ width: 10rem; }}"
|
||||
$"#{nameof model.State} {{ width: 3rem; }}"
|
||||
$"#{nameof model.InterfaceAddress} {{ width: 30rem; }}"
|
||||
]
|
||||
|> AppViewInfo.withOnLoadScript "PT.church.edit.onPageLoad"
|
||||
form [ _action "/church/save"; _method "post"; _class "pt-center-columns"; Target.content ] [
|
||||
|
@ -22,40 +22,29 @@ let edit (model : EditChurch) ctx viewInfo =
|
|||
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 ]
|
||||
label [ _for (nameof model.Name) ] [ locStr s["Church Name"] ]
|
||||
inputField "text" (nameof model.Name) model.Name [ _required; _autofocus ]
|
||||
]
|
||||
div [ _inputField ] [
|
||||
label [ _for "City"] [ locStr s["City"] ]
|
||||
input [ _type "text"; _name (nameof model.City); _id "city"; _required; _value model.City ]
|
||||
label [ _for (nameof model.City) ] [ locStr s["City"] ]
|
||||
inputField "text" (nameof model.City) model.City [ _required ]
|
||||
]
|
||||
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 ]
|
||||
label [ _for (nameof model.State) ] [ locStr s["State or Province"] ]
|
||||
inputField "text" (nameof model.State) model.State [ _minlength "2"; _maxlength "2"; _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"] ]
|
||||
inputField "checkbox" (nameof model.HasInterface) "True"
|
||||
[ if defaultArg model.HasInterface false then _checked ]
|
||||
label [ _for (nameof model.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 "") ]
|
||||
label [ _for (nameof model.InterfaceAddress) ] [ locStr s["VPR Interface URL"] ]
|
||||
inputField "url" (nameof model.InterfaceAddress) (defaultArg model.InterfaceAddress "") []
|
||||
]
|
||||
]
|
||||
div [ _fieldRow ] [ submit [] "save" s["Save Church"] ]
|
||||
|
@ -73,17 +62,7 @@ let maintain (churches : Church list) (stats : Map<string, ChurchStats>) ctx vi
|
|||
| [] -> space
|
||||
| _ ->
|
||||
table [ _class "pt-table pt-action-table" ] [
|
||||
thead [] [
|
||||
tr [] [
|
||||
th [] [ locStr s["Actions"] ]
|
||||
th [] [ locStr s["Name"] ]
|
||||
th [] [ locStr s["Location"] ]
|
||||
th [] [ locStr s["Groups"] ]
|
||||
th [] [ locStr s["Requests"] ]
|
||||
th [] [ locStr s["Users"] ]
|
||||
th [] [ locStr s["Interface?"] ]
|
||||
]
|
||||
]
|
||||
tableHeadings s [ "Actions"; "Name"; "Location"; "Groups"; "Requests"; "Users"; "Interface?" ]
|
||||
churches
|
||||
|> List.map (fun ch ->
|
||||
let chId = flatGuid ch.churchId
|
||||
|
|
|
@ -138,6 +138,21 @@ let _inputField = _inputFieldWith []
|
|||
/// The class that designates a checkbox / label pair
|
||||
let _checkboxField = _class "pt-checkbox-field"
|
||||
|
||||
/// Create an input field of the given type, with matching name and ID and the given value
|
||||
let inputField typ name value attrs =
|
||||
List.concat [ [ _type typ; _name name; _id name; if value <> "" then _value value ]; attrs ] |> input
|
||||
|
||||
/// Generate a table heading with the given localized column names
|
||||
let tableHeadings (s : IStringLocalizer) (headings : string list) =
|
||||
headings
|
||||
|> List.map (fun heading -> th [ _scope "col" ] [ locStr s[heading] ])
|
||||
|> tr []
|
||||
|> List.singleton
|
||||
|> thead []
|
||||
|
||||
/// For a list of strings, prepend a pound sign and string them together with commas (CSS selector by ID)
|
||||
let toHtmlIds it = it |> List.map (fun x -> $"#%s{x}") |> String.concat ", "
|
||||
|
||||
/// The name this function used to have when the view engine was part of Giraffe
|
||||
let renderHtmlNode = RenderView.AsString.htmlNode
|
||||
|
||||
|
|
|
@ -198,13 +198,13 @@ let private commonHead = [
|
|||
]
|
||||
|
||||
/// Render the <head> portion of the page
|
||||
let private htmlHead m pageTitle =
|
||||
let private htmlHead viewInfo pgTitle =
|
||||
let s = I18N.localizer.Force ()
|
||||
head [] [
|
||||
meta [ _charset "UTF-8" ]
|
||||
title [] [ locStr pageTitle; titleSep; locStr s["PrayerTracker"] ]
|
||||
title [] [ locStr pgTitle; titleSep; locStr s["PrayerTracker"] ]
|
||||
yield! commonHead
|
||||
for cssFile in m.Style do
|
||||
for cssFile in viewInfo.Style do
|
||||
link [ _rel "stylesheet"; _href $"/css/{cssFile}.css"; _type "text/css" ]
|
||||
]
|
||||
|
||||
|
@ -224,16 +224,18 @@ let private helpLink link =
|
|||
]
|
||||
|
||||
/// Render the page title, and optionally a help link
|
||||
let private renderPageTitle m pageTitle =
|
||||
let private renderPageTitle viewInfo pgTitle =
|
||||
h2 [ _id "pt-page-title" ] [
|
||||
match m.HelpLink with Some link -> PrayerTracker.Utils.Help.fullLink (langCode ()) link |> helpLink | None -> ()
|
||||
locStr pageTitle
|
||||
match viewInfo.HelpLink with
|
||||
| Some link -> PrayerTracker.Utils.Help.fullLink (langCode ()) link |> helpLink
|
||||
| None -> ()
|
||||
locStr pgTitle
|
||||
]
|
||||
|
||||
/// Render the messages that may need to be displayed to the user
|
||||
let private messages m =
|
||||
let private messages viewInfo =
|
||||
let s = I18N.localizer.Force ()
|
||||
m.Messages
|
||||
viewInfo.Messages
|
||||
|> List.map (fun msg ->
|
||||
table [ _class $"pt-msg {MessageLevel.toCssClass msg.Level}" ] [
|
||||
tr [] [
|
||||
|
@ -257,10 +259,10 @@ let private messages m =
|
|||
open System
|
||||
|
||||
/// Render the <footer> at the bottom of the page
|
||||
let private htmlFooter m =
|
||||
let private htmlFooter viewInfo =
|
||||
let s = I18N.localizer.Force ()
|
||||
let imgText = $"""%O{s["PrayerTracker"]} %O{s["from Bit Badger Solutions"]}"""
|
||||
let resultTime = TimeSpan(DateTime.Now.Ticks - m.RequestStart).TotalSeconds
|
||||
let resultTime = TimeSpan(DateTime.Now.Ticks - viewInfo.RequestStart).TotalSeconds
|
||||
footer [] [
|
||||
div [ _id "pt-legal" ] [
|
||||
a [ _href "/legal/privacy-policy" ] [ locStr s["Privacy Policy"] ]
|
||||
|
@ -278,7 +280,7 @@ let private htmlFooter m =
|
|||
a [ _href "/"; _style "line-height:28px;" ] [
|
||||
img [ _src $"""/img/%O{s["footer_en"]}.png"""; _alt imgText; _title imgText ]
|
||||
]
|
||||
str m.Version
|
||||
str viewInfo.Version
|
||||
space
|
||||
i [ _title s["This page loaded in {0:N3} seconds", resultTime].Value; _class "material-icons md-18" ] [
|
||||
str "schedule"
|
||||
|
@ -289,9 +291,9 @@ let private htmlFooter m =
|
|||
]
|
||||
|
||||
/// The content portion of the PrayerTracker layout
|
||||
let private contentSection viewInfo title (content : XmlNode) = [
|
||||
let private contentSection viewInfo pgTitle (content : XmlNode) = [
|
||||
Navigation.identity viewInfo
|
||||
renderPageTitle viewInfo title
|
||||
renderPageTitle viewInfo pgTitle
|
||||
yield! messages viewInfo
|
||||
match viewInfo.ScopedStyle with
|
||||
| [] -> ()
|
||||
|
@ -301,7 +303,7 @@ let private contentSection viewInfo title (content : XmlNode) = [
|
|||
for jsFile in viewInfo.Script do
|
||||
script [ _src $"/js/{jsFile}.js" ] []
|
||||
match viewInfo.OnLoadScript with
|
||||
| Some onLoad -> script [] [ rawText $"PT.onLoad({onLoad}" ]
|
||||
| Some onLoad -> script [] [ rawText $"{onLoad}()" ]
|
||||
| None -> ()
|
||||
]
|
||||
|
||||
|
@ -316,11 +318,11 @@ let private partialHead pgTitle =
|
|||
open Giraffe.Htmx.Common
|
||||
|
||||
/// The body of the PrayerTracker layout
|
||||
let private pageLayout viewInfo title content =
|
||||
let private pageLayout viewInfo pgTitle content =
|
||||
body [ _hxBoost ] [
|
||||
Navigation.top viewInfo
|
||||
div [ _id "pt-body"; Target.content; _hxSwap $"{HxSwap.InnerHtml} show:window:top" ]
|
||||
(contentSection viewInfo title content)
|
||||
(contentSection viewInfo pgTitle content)
|
||||
]
|
||||
|
||||
/// The standard layout(s) for PrayerTracker
|
||||
|
|
|
@ -17,7 +17,7 @@ let edit (model : EditRequest) today ctx viewInfo =
|
|||
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) ]
|
||||
inputField "hidden" (nameof model.RequestId) (flatGuid model.RequestId) []
|
||||
div [ _fieldRow ] [
|
||||
div [ _inputField ] [
|
||||
label [ _for (nameof model.RequestType) ] [ locStr s["Request Type"] ]
|
||||
|
@ -27,27 +27,21 @@ let edit (model : EditRequest) today ctx viewInfo =
|
|||
|> 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 "") ]
|
||||
label [ _for (nameof model.Requestor) ] [ locStr s["Requestor / Subject"] ]
|
||||
inputField "text" (nameof model.Requestor) (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 ]
|
||||
label [ _for (nameof model.EnteredDate) ] [ locStr s["Date"] ]
|
||||
inputField "date" (nameof model.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"] ]
|
||||
inputField "checkbox" (nameof model.SkipDateUpdate) "True" []
|
||||
label [ _for (nameof model.SkipDateUpdate) ] [ locStr s["Check to not update the date"] ]
|
||||
br []
|
||||
small [] [ em [] [ str (s["Typo Corrections"].Value.ToLower ()); rawText ", etc." ] ]
|
||||
]
|
||||
|
@ -58,7 +52,7 @@ let edit (model : EditRequest) today ctx viewInfo =
|
|||
label [] [ locStr s["Expiration"] ]
|
||||
ReferenceList.expirationList s (not model.IsNew)
|
||||
|> List.map (fun exp ->
|
||||
let radioId = $"expiration_{fst exp}"
|
||||
let radioId = String.concat "_" [ nameof model.Expiration; fst exp ]
|
||||
span [ _class "text-nowrap" ] [
|
||||
radio (nameof model.Expiration) radioId (fst exp) model.Expiration
|
||||
label [ _for radioId ] [ locStr (snd exp) ]
|
||||
|
@ -69,8 +63,8 @@ let edit (model : EditRequest) today ctx viewInfo =
|
|||
]
|
||||
div [ _fieldRow ] [
|
||||
div [ _inputFieldWith [ "pt-editor" ] ] [
|
||||
label [ _for "text" ] [ locStr s["Request"] ]
|
||||
textarea [ _name (nameof model.Text); _id "text" ] [ str model.Text ]
|
||||
label [ _for (nameof model.Text) ] [ locStr s["Request"] ]
|
||||
textarea [ _name (nameof model.Text); _id (nameof model.Text) ] [ str model.Text ]
|
||||
]
|
||||
]
|
||||
div [ _fieldRow ] [ submit [] "save" s["Save Request"] ]
|
||||
|
@ -129,13 +123,7 @@ let lists (groups : SmallGroup list) viewInfo =
|
|||
| count ->
|
||||
tableSummary count s
|
||||
table [ _class "pt-table pt-action-table" ] [
|
||||
thead [] [
|
||||
tr [] [
|
||||
th [] [ locStr s["Actions"] ]
|
||||
th [] [ locStr s["Church"] ]
|
||||
th [] [ locStr s["Group"] ]
|
||||
]
|
||||
]
|
||||
tableHeadings s [ "Actions"; "Church"; "Group" ]
|
||||
groups
|
||||
|> List.map (fun grp ->
|
||||
let grpId = flatGuid grp.smallGroupId
|
||||
|
@ -238,11 +226,7 @@ let maintain (model : MaintainRequests) (ctx : HttpContext) viewInfo =
|
|||
| None -> ()
|
||||
]
|
||||
form [ _action "/prayer-requests"; _method "get"; _class "pt-center-text pt-search-form"; Target.content ] [
|
||||
input [ _type "text"
|
||||
_name "search"
|
||||
_placeholder l["Search requests..."].Value
|
||||
_value (defaultArg model.SearchTerm "")
|
||||
]
|
||||
inputField "text" "search" (defaultArg model.SearchTerm "") [ _placeholder l["Search requests..."].Value ]
|
||||
space
|
||||
submit [] "search" s["Search"]
|
||||
]
|
||||
|
@ -252,15 +236,7 @@ let maintain (model : MaintainRequests) (ctx : HttpContext) viewInfo =
|
|||
| 0 -> ()
|
||||
| _ ->
|
||||
table [ _class "pt-table pt-action-table" ] [
|
||||
thead [] [
|
||||
tr [] [
|
||||
th [] [ locStr s["Actions"] ]
|
||||
th [] [ locStr s["Updated Date"] ]
|
||||
th [] [ locStr s["Type"] ]
|
||||
th [] [ locStr s["Requestor"] ]
|
||||
th [] [ locStr s["Request"] ]
|
||||
]
|
||||
]
|
||||
tableHeadings s [ "Actions"; "Updated Date"; "Type"; "Requestor"; "Request"]
|
||||
tbody [] requests
|
||||
]
|
||||
div [ _class "pt-center-text" ] [
|
||||
|
|
|
@ -8,15 +8,15 @@ open PrayerTracker.ViewModels
|
|||
/// View for the announcement page
|
||||
let announcement isAdmin ctx viewInfo =
|
||||
let s = I18N.localizer.Force ()
|
||||
let model = { SendToClass = ""; Text = ""; AddToRequestList = None; RequestType = None }
|
||||
let model = { SendToClass = ""; Text = ""; AddToRequestList = None; RequestType = None }
|
||||
let reqTypes = ReferenceList.requestTypeList s
|
||||
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 ] []
|
||||
label [ _for (nameof model.Text) ] [ locStr s["Announcement Text"] ]
|
||||
textarea [ _name (nameof model.Text); _id (nameof model.Text); _autofocus ] []
|
||||
]
|
||||
]
|
||||
if isAdmin then
|
||||
|
@ -24,18 +24,20 @@ let announcement isAdmin ctx viewInfo =
|
|||
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"]] ]
|
||||
radio (nameof model.SendToClass) $"{nameof model.SendToClass}_Y" "Y" "Y"
|
||||
label [ _for $"{nameof model.SendToClass}_Y" ] [
|
||||
locStr s["This Group"]; rawText " "
|
||||
]
|
||||
radio (nameof model.SendToClass) $"{nameof model.SendToClass}_N" "N" "Y"
|
||||
label [ _for $"{nameof model.SendToClass}_N" ] [ locStr s["All {0} Users", s["PrayerTracker"]] ]
|
||||
]
|
||||
]
|
||||
]
|
||||
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"] ]
|
||||
inputField "checkbox" (nameof model.AddToRequestList) "True" []
|
||||
label [ _for (nameof model.AddToRequestList) ] [ locStr s["Add to Request List"] ]
|
||||
]
|
||||
]
|
||||
div [ _fieldRowWith [ "pt-fadeable" ]; _id "divCategory" ] [
|
||||
|
@ -74,11 +76,11 @@ let edit (model : EditSmallGroup) (churches : Church list) ctx viewInfo =
|
|||
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 model.SmallGroupId); _value (flatGuid model.SmallGroupId) ]
|
||||
inputField "hidden" (nameof model.SmallGroupId) (flatGuid model.SmallGroupId) []
|
||||
div [ _fieldRow ] [
|
||||
div [ _inputField ] [
|
||||
label [ _for "name" ] [ locStr s["Group Name"] ]
|
||||
input [ _type "text"; _name (nameof model.Name); _id "name"; _value model.Name; _required; _autofocus ]
|
||||
label [ _for (nameof model.Name) ] [ locStr s["Group Name"] ]
|
||||
inputField "text" (nameof model.Name) model.Name [ _required; _autofocus ]
|
||||
]
|
||||
]
|
||||
div [ _fieldRow ] [
|
||||
|
@ -102,18 +104,22 @@ let edit (model : EditSmallGroup) (churches : Church list) ctx viewInfo =
|
|||
let editMember (model : EditMember) (types : (string * LocalizedString) seq) ctx viewInfo =
|
||||
let s = I18N.localizer.Force ()
|
||||
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
|
||||
let vi =
|
||||
AppViewInfo.withScopedStyles [
|
||||
$"#{nameof model.Name} {{ width: 15rem; }}"
|
||||
$"#{nameof model.Email} {{ width: 20rem; }}"
|
||||
] viewInfo
|
||||
form [ _action "/small-group/member/save"; _method "post"; _class "pt-center-columns"; Target.content ] [
|
||||
csrfToken ctx
|
||||
input [ _type "hidden"; _name (nameof model.MemberId); _value (flatGuid model.MemberId) ]
|
||||
inputField "hidden" (nameof model.MemberId) (flatGuid model.MemberId) []
|
||||
div [ _fieldRow ] [
|
||||
div [ _inputField ] [
|
||||
label [ _for "name" ] [ locStr s["Member Name"] ]
|
||||
input [ _type "text"; _name (nameof model.Name); _id "name"; _required; _autofocus; _value model.Name ]
|
||||
label [ _for (nameof model.Name) ] [ locStr s["Member Name"] ]
|
||||
inputField "text" (nameof model.Name) model.Name [ _required; _autofocus ]
|
||||
]
|
||||
div [ _inputField ] [
|
||||
label [ _for "email" ] [ locStr s["E-mail Address"] ]
|
||||
input [ _type "email"; _name (nameof model.Email); _id "email"; _required; _value model.Email ]
|
||||
label [ _for (nameof model.Email) ] [ locStr s["E-mail Address"] ]
|
||||
inputField "email" (nameof model.Email) model.Email [ _required ]
|
||||
]
|
||||
]
|
||||
div [ _fieldRow ] [
|
||||
|
@ -152,17 +158,14 @@ let logOn (groups : SmallGroup list) grpId ctx viewInfo =
|
|||
|> selectList (nameof model.SmallGroupId) grpId [ _required ]
|
||||
]
|
||||
div [ _inputField ] [
|
||||
label [ _for "password" ] [ locStr s["Password"] ]
|
||||
input [ _type "password"
|
||||
_name (nameof model.Password)
|
||||
_id "password"
|
||||
_placeholder (s["Case-Sensitive"].Value.ToLower ())
|
||||
_required ]
|
||||
label [ _for (nameof model.Password) ] [ locStr s["Password"] ]
|
||||
inputField "password" (nameof model.Password) ""
|
||||
[ _placeholder (s["Case-Sensitive"].Value.ToLower ()); _required ]
|
||||
]
|
||||
]
|
||||
div [ _checkboxField ] [
|
||||
input [ _type "checkbox"; _name (nameof model.RememberMe); _id "rememberMe"; _value "True" ]
|
||||
label [ _for "rememberMe" ] [ locStr s["Remember Me"] ]
|
||||
inputField "checkbox" (nameof model.RememberMe) "True" []
|
||||
label [ _for (nameof model.RememberMe) ] [ locStr s["Remember Me"] ]
|
||||
br []
|
||||
small [] [ em [] [ str (s["Requires Cookies"].Value.ToLower ()) ] ]
|
||||
]
|
||||
|
@ -181,14 +184,7 @@ let maintain (groups : SmallGroup list) ctx viewInfo =
|
|||
| [] -> space
|
||||
| _ ->
|
||||
table [ _class "pt-table pt-action-table" ] [
|
||||
thead [] [
|
||||
tr [] [
|
||||
th [] [ locStr s["Actions"] ]
|
||||
th [] [ locStr s["Name"] ]
|
||||
th [] [ locStr s["Church"] ]
|
||||
th [] [ locStr s["Time Zone"] ]
|
||||
]
|
||||
]
|
||||
tableHeadings s [ "Actions"; "Name"; "Church"; "Time Zone"]
|
||||
groups
|
||||
|> List.map (fun g ->
|
||||
let grpId = flatGuid g.smallGroupId
|
||||
|
@ -234,14 +230,7 @@ let members (members : Member list) (emailTypes : Map<string, LocalizedString>)
|
|||
| [] -> space
|
||||
| _ ->
|
||||
table [ _class "pt-table pt-action-table" ] [
|
||||
thead [] [
|
||||
tr [] [
|
||||
th [] [ locStr s["Actions"] ]
|
||||
th [] [ locStr s["Name"] ]
|
||||
th [] [ locStr s["E-mail Address"] ]
|
||||
th [] [ locStr s["Format"] ]
|
||||
]
|
||||
]
|
||||
tableHeadings s [ "Actions"; "Name"; "E-mail Address"; "Format"]
|
||||
members
|
||||
|> List.map (fun mbr ->
|
||||
let mbrId = flatGuid mbr.memberId
|
||||
|
@ -348,10 +337,16 @@ let preferences (model : EditPreferences) (tzs : TimeZone list) ctx viewInfo =
|
|||
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%; } }"
|
||||
let numberFields =
|
||||
[ nameof model.ExpireDays; nameof model.DaysToKeepNew; nameof model.LongTermUpdateWeeks
|
||||
nameof model.HeadingFontSize; nameof model.ListFontSize; nameof model.PageSize
|
||||
]
|
||||
|> toHtmlIds
|
||||
let fontsId = $"#{nameof model.Fonts}"
|
||||
$"{numberFields} {{ width: 3rem; }}"
|
||||
$"#{nameof model.EmailFromAddress} {{ width: 20rem; }}"
|
||||
$"{fontsId} {{ width: 40rem; }}"
|
||||
$"@media screen and (max-width: 40rem) {{ {fontsId} {{ width: 100%%; }} }}"
|
||||
]
|
||||
|> AppViewInfo.withOnLoadScript "PT.smallGroup.preferences.onPageLoad"
|
||||
form [ _action "/small-group/preferences/save"; _method "post"; _class "pt-center-columns"; Target.content ] [
|
||||
|
@ -360,40 +355,32 @@ let preferences (model : EditPreferences) (tzs : TimeZone list) ctx viewInfo =
|
|||
legend [] [ strong [] [ icon "date_range"; rawText " "; locStr s["Dates"] ] ]
|
||||
div [ _fieldRow ] [
|
||||
div [ _inputField ] [
|
||||
label [ _for "expireDays" ] [ locStr s["Requests Expire After"] ]
|
||||
label [ _for (nameof model.ExpireDays) ] [ locStr s["Requests Expire After"] ]
|
||||
span [] [
|
||||
input [ _type "number"
|
||||
_name (nameof model.ExpireDays)
|
||||
_id "expireDays"
|
||||
_value (string model.ExpireDays)
|
||||
_min "1"; _max "30"
|
||||
_required
|
||||
_autofocus ]
|
||||
inputField "number" (nameof model.ExpireDays) (string model.ExpireDays) [
|
||||
_min "1"; _max "30"; _required; _autofocus
|
||||
]
|
||||
space
|
||||
str (s["Days"].Value.ToLower ())
|
||||
]
|
||||
]
|
||||
div [ _inputField ] [
|
||||
label [ _for "daysToKeepNew" ] [ locStr s["Requests “New” For"] ]
|
||||
label [ _for (nameof model.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 ]
|
||||
inputField "number" (nameof model.DaysToKeepNew) (string model.DaysToKeepNew) [
|
||||
_min "1"; _max "30"; _required
|
||||
]
|
||||
space; str (s["Days"].Value.ToLower ())
|
||||
]
|
||||
]
|
||||
div [ _inputField ] [
|
||||
label [ _for "longTermUpdateWeeks" ] [ locStr s["Long-Term Requests Alerted for Update"] ]
|
||||
label [ _for (nameof model.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 ]
|
||||
inputField "number" (nameof model.LongTermUpdateWeeks) (string model.LongTermUpdateWeeks) [
|
||||
_min "1"; _max "30"; _required
|
||||
]
|
||||
space; str (s["Weeks"].Value.ToLower ())
|
||||
]
|
||||
]
|
||||
|
@ -401,30 +388,22 @@ let preferences (model : EditPreferences) (tzs : TimeZone list) ctx viewInfo =
|
|||
]
|
||||
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"] ]
|
||||
radio (nameof model.RequestSort) $"{nameof model.RequestSort}_D" "D" model.RequestSort
|
||||
label [ _for $"{nameof model.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"] ]
|
||||
radio (nameof model.RequestSort) $"{nameof model.RequestSort}_R" "R" model.RequestSort
|
||||
label [ _for $"{nameof model.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 ]
|
||||
label [ _for (nameof model.EmailFromName) ] [ locStr s["From Name"] ]
|
||||
inputField "text" (nameof model.EmailFromName) 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 ]
|
||||
label [ _for (nameof model.EmailFromAddress) ] [ locStr s["From Address"] ]
|
||||
inputField "email" (nameof model.EmailFromAddress) model.EmailFromAddress [ _required ]
|
||||
]
|
||||
]
|
||||
div [ _fieldRow ] [
|
||||
|
@ -447,17 +426,18 @@ let preferences (model : EditPreferences) (tzs : TimeZone list) ctx viewInfo =
|
|||
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"] ]
|
||||
radio (nameof model.LineColorType) $"{nameof model.LineColorType}_Name" "Name"
|
||||
model.LineColorType
|
||||
label [ _for $"{nameof model.LineColorType}_Name" ] [ locStr s["Named Color"] ]
|
||||
namedColorList (nameof model.LineColor) model.LineColor [
|
||||
_id "lineColor_Select"
|
||||
_id $"{nameof model.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"] ]
|
||||
radio (nameof model.LineColorType) $"{nameof model.LineColorType}_RGB" "RGB" model.LineColorType
|
||||
label [ _for $"{nameof model.LineColorType}_RGB" ] [ locStr s["Custom Color"] ]
|
||||
input [ _type "color"
|
||||
_name (nameof model.LineColor)
|
||||
_id "lineColor_Color"
|
||||
_id $"{nameof model.LineColor}_Color"
|
||||
_value model.LineColor // TODO: convert to hex or skip if named
|
||||
if not (model.LineColor.StartsWith "#") then _disabled ]
|
||||
]
|
||||
|
@ -467,17 +447,19 @@ let preferences (model : EditPreferences) (tzs : TimeZone list) ctx viewInfo =
|
|||
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"] ]
|
||||
radio (nameof model.HeadingColorType) $"{nameof model.HeadingColorType}_Name" "Name"
|
||||
model.HeadingColorType
|
||||
label [ _for $"{nameof model.HeadingColorType}_Name" ] [ locStr s["Named Color"] ]
|
||||
namedColorList (nameof model.HeadingColor) model.HeadingColor [
|
||||
_id "headingColor_Select"
|
||||
_id $"{nameof model.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"] ]
|
||||
radio (nameof model.HeadingColorType) $"{nameof model.HeadingColorType}_RGB" "RGB"
|
||||
model.HeadingColorType
|
||||
label [ _for $"{nameof model.HeadingColorType}_RGB" ] [ locStr s["Custom Color"] ]
|
||||
input [ _type "color"
|
||||
_name (nameof model.HeadingColor)
|
||||
_id "headingColor_Color"
|
||||
_id $"{nameof model.HeadingColor}_Color"
|
||||
_value model.HeadingColor // TODO: convert to hex or skip if named
|
||||
if not (model.HeadingColor.StartsWith "#") then _disabled ]
|
||||
]
|
||||
|
@ -487,27 +469,21 @@ let preferences (model : EditPreferences) (tzs : TimeZone list) ctx viewInfo =
|
|||
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 ]
|
||||
label [ _for (nameof model.Fonts) ] [ locStr s["Fonts** for List"] ]
|
||||
inputField "text" (nameof model.Fonts) model.Fonts [ _required ]
|
||||
]
|
||||
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 ]
|
||||
label [ _for (nameof model.HeadingFontSize) ] [ locStr s["Heading Text Size"] ]
|
||||
inputField "number" (nameof model.HeadingFontSize) (string model.HeadingFontSize) [
|
||||
_min "8"; _max "24"; _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 ]
|
||||
label [ _for (nameof model.ListFontSize) ] [ locStr s["List Text Size"] ]
|
||||
inputField "number" (nameof model.ListFontSize) (string model.ListFontSize) [
|
||||
_min "8"; _max "24"; _required
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
@ -526,38 +502,31 @@ let preferences (model : EditPreferences) (tzs : TimeZone list) ctx viewInfo =
|
|||
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"] ]
|
||||
let name = nameof model.Visibility
|
||||
let value = string model.Visibility
|
||||
radio name $"{name}_Public" (string RequestVisibility.``public``) value
|
||||
label [ _for $"{name}_Public" ] [ locStr s["Public"] ]
|
||||
rawText " "
|
||||
radio (nameof model.Visibility) "viz_Private" (string RequestVisibility.``private``)
|
||||
(string model.Visibility)
|
||||
label [ _for "viz_Private" ] [ locStr s["Private"] ]
|
||||
radio name $"{name}_Private" (string RequestVisibility.``private``) value
|
||||
label [ _for $"{name}_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"] ]
|
||||
radio name $"{name}_Password" (string RequestVisibility.passwordProtected) value
|
||||
label [ _for $"{name}_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 "") ]
|
||||
label [ _for (nameof model.GroupPassword) ] [ locStr s["Group Password (Used to Read Online)"] ]
|
||||
inputField "text" (nameof model.GroupPassword) (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 ]
|
||||
label [ _for (nameof model.PageSize) ] [ locStr s["Page Size"] ]
|
||||
inputField "number" (nameof model.PageSize) (string model.PageSize) [
|
||||
_min "10"; _max "255"; _required
|
||||
]
|
||||
]
|
||||
div [ _inputField ] [
|
||||
label [ _for (nameof model.AsOfDate) ] [ locStr s["“As of” Date Display"] ]
|
||||
|
|
|
@ -9,8 +9,8 @@ let assignGroups model groups curGroups ctx viewInfo =
|
|||
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 model.UserId); _value (flatGuid model.UserId) ]
|
||||
input [ _type "hidden"; _name (nameof model.UserName); _value model.UserName ]
|
||||
inputField "hidden" (nameof model.UserId) (flatGuid model.UserId) []
|
||||
inputField "hidden" (nameof model.UserName) model.UserName []
|
||||
table [ _class "pt-table" ] [
|
||||
thead [] [
|
||||
tr [] [
|
||||
|
@ -42,10 +42,15 @@ let assignGroups model groups curGroups ctx viewInfo =
|
|||
|
||||
/// View for the password change page
|
||||
let changePassword ctx viewInfo =
|
||||
let s = I18N.localizer.Force ()
|
||||
let s = I18N.localizer.Force ()
|
||||
let model = { OldPassword = ""; NewPassword = ""; NewPasswordConfirm = "" }
|
||||
let vi =
|
||||
AppViewInfo.withScopedStyles [ "#oldPassword, #newPassword, #newPasswordConfirm { width: 10rem; }"] viewInfo
|
||||
let vi =
|
||||
viewInfo
|
||||
|> AppViewInfo.withScopedStyles [
|
||||
let fields =
|
||||
toHtmlIds [ nameof model.OldPassword; nameof model.NewPassword; nameof model.NewPasswordConfirm ]
|
||||
$"{fields} {{ width: 10rem; }}"
|
||||
]
|
||||
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."]
|
||||
]
|
||||
|
@ -53,35 +58,28 @@ let changePassword ctx viewInfo =
|
|||
|> List.append [
|
||||
form [ _action "/user/password/change"
|
||||
_method "post"
|
||||
_onsubmit $"""return PT.compareValidation('newPassword','newPasswordConfirm','%A{s["The passwords do not match"]}')"""
|
||||
_onsubmit $"""return PT.compareValidation('{nameof model.NewPassword}','{nameof model.NewPasswordConfirm}','%A{s["The passwords do not match"]}')"""
|
||||
Target.content ] [
|
||||
csrfToken ctx
|
||||
div [ _fieldRow ] [
|
||||
div [ _inputField ] [
|
||||
label [ _for "oldPassword" ] [ locStr s["Current Password"] ]
|
||||
input [ _type "password"
|
||||
_name (nameof model.OldPassword)
|
||||
_id "oldPassword"
|
||||
_required
|
||||
_autofocus ]
|
||||
label [ _for (nameof model.OldPassword) ] [ locStr s["Current Password"] ]
|
||||
inputField "password" (nameof model.OldPassword) "" [ _required; _autofocus ]
|
||||
]
|
||||
]
|
||||
div [ _fieldRow ] [
|
||||
div [ _inputField ] [
|
||||
label [ _for "newPassword" ] [ locStr s["New Password Twice"] ]
|
||||
input [ _type "password"; _name (nameof model.NewPassword); _id "newPassword"; _required ]
|
||||
label [ _for (nameof model.NewPassword) ] [ locStr s["New Password Twice"] ]
|
||||
inputField "password" (nameof model.NewPassword) "" [ _required ]
|
||||
]
|
||||
div [ _inputField ] [
|
||||
label [] [ rawText " " ]
|
||||
input [ _type "password"
|
||||
_name (nameof model.NewPasswordConfirm)
|
||||
_id "newPasswordConfirm"
|
||||
_required ]
|
||||
label [ _for (nameof model.NewPasswordConfirm) ] [ rawText " " ]
|
||||
inputField "password" (nameof model.NewPasswordConfirm) "" [ _required ]
|
||||
]
|
||||
]
|
||||
div [ _fieldRow ] [
|
||||
submit [ _onclick "document.getElementById('newPasswordConfirm').setCustomValidity('')" ] "done"
|
||||
s["Change Your Password"]
|
||||
submit [ _onclick $"document.getElementById('{nameof model.NewPasswordConfirm}').setCustomValidity('')" ]
|
||||
"done" s["Change Your Password"]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
@ -94,59 +92,50 @@ let edit (model : EditUser) ctx viewInfo =
|
|||
let s = I18N.localizer.Force ()
|
||||
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 =
|
||||
let vi =
|
||||
viewInfo
|
||||
|> AppViewInfo.withScopedStyles [
|
||||
"#firstName, #lastName, #password, #passwordConfirm { width: 10rem; }"
|
||||
"#email { width: 20rem; }"
|
||||
let fields =
|
||||
[ nameof model.FirstName; nameof model.LastName; nameof model.Password; nameof model.PasswordConfirm ]
|
||||
|> toHtmlIds
|
||||
$"{fields} {{ width: 10rem; }}"
|
||||
$"#{nameof model.Email} {{ width: 20rem; }}"
|
||||
]
|
||||
|> 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"]}')"""
|
||||
_onsubmit $"""return PT.compareValidation('{nameof model.Password}','{nameof model.PasswordConfirm}','%A{s["The passwords do not match"]}')"""
|
||||
Target.content ] [
|
||||
csrfToken ctx
|
||||
input [ _type "hidden"; _name (nameof model.UserId); _value (flatGuid model.UserId) ]
|
||||
inputField "hidden" (nameof model.UserId) (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 ]
|
||||
label [ _for (nameof model.FirstName) ] [ locStr s["First Name"] ]
|
||||
inputField "text" (nameof model.FirstName) 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 ]
|
||||
label [ _for (nameof model.LastName) ] [ locStr s["Last Name"] ]
|
||||
inputField "text" (nameof model.LastName) 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 ]
|
||||
label [ _for (nameof model.Email) ] [ locStr s["E-mail Address"] ]
|
||||
inputField "email" (nameof model.Email) model.Email [ _required ]
|
||||
]
|
||||
]
|
||||
div [ _fieldRow ] [
|
||||
div [ _inputField ] [
|
||||
label [ _for "password" ] [ locStr s["Password"] ]
|
||||
input [ _type "password"; _name (nameof model.Password); _id "password"; _placeholder pwPlaceholder ]
|
||||
label [ _for (nameof model.Password) ] [ locStr s["Password"] ]
|
||||
inputField "password" (nameof model.Password) "" [ _placeholder pwPlaceholder ]
|
||||
]
|
||||
div [ _inputField ] [
|
||||
label [ _for "passwordConfirm" ] [ locStr s["Password Again"] ]
|
||||
input [ _type "password"
|
||||
_name (nameof model.PasswordConfirm)
|
||||
_id "passwordConfirm"
|
||||
_placeholder pwPlaceholder ]
|
||||
inputField "password" (nameof model.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"] ]
|
||||
inputField "checkbox" (nameof model.IsAdmin) "True" [ if defaultArg model.IsAdmin false then _checked ]
|
||||
label [ _for (nameof model.IsAdmin) ] [ locStr s["This user is a PrayerTracker administrator"] ]
|
||||
]
|
||||
div [ _fieldRow ] [ submit [] "save" s["Save User"] ]
|
||||
]
|
||||
|
@ -160,24 +149,17 @@ let logOn (model : UserLogOn) groups ctx viewInfo =
|
|||
let vi = AppViewInfo.withScopedStyles [ "#email { width: 20rem; }" ] viewInfo
|
||||
form [ _action "/user/log-on"; _method "post"; _class "pt-center-columns"; Target.body ] [
|
||||
csrfToken ctx
|
||||
input [ _type "hidden"; _name (nameof model.RedirectUrl); _value (defaultArg model.RedirectUrl "") ]
|
||||
inputField "hidden" (nameof model.RedirectUrl) (defaultArg model.RedirectUrl "") []
|
||||
div [ _fieldRow ] [
|
||||
div [ _inputField ] [
|
||||
label [ _for "email"] [ locStr s["E-mail Address"] ]
|
||||
input [ _type "email"
|
||||
_name (nameof model.Email)
|
||||
_id "email"
|
||||
_value model.Email
|
||||
_required
|
||||
_autofocus ]
|
||||
label [ _for (nameof model.Email) ] [ locStr s["E-mail Address"] ]
|
||||
inputField "email" (nameof model.Email) model.Email [ _required; _autofocus ]
|
||||
]
|
||||
div [ _inputField ] [
|
||||
label [ _for "password" ] [ locStr s["Password"] ]
|
||||
input [ _type "password"
|
||||
_name (nameof model.Password)
|
||||
_id "password"
|
||||
_placeholder $"""({s["Case-Sensitive"].Value.ToLower ()})"""
|
||||
_required ]
|
||||
label [ _for (nameof model.Password) ] [ locStr s["Password"] ]
|
||||
inputField "password" (nameof model.Password) "" [
|
||||
_placeholder $"""({s["Case-Sensitive"].Value.ToLower ()})"""; _required
|
||||
]
|
||||
]
|
||||
]
|
||||
div [ _fieldRow ] [
|
||||
|
@ -188,7 +170,7 @@ let logOn (model : UserLogOn) groups ctx viewInfo =
|
|||
]
|
||||
]
|
||||
div [ _checkboxField ] [
|
||||
input [ _type "checkbox"; _name (nameof model.RememberMe); _id "rememberMe"; _value "True" ]
|
||||
inputField "checkbox" (nameof model.RememberMe) "True" []
|
||||
label [ _for "rememberMe" ] [ locStr s["Remember Me"] ]
|
||||
br []
|
||||
small [] [ em [] [ str $"""({s["Requires Cookies"].Value.ToLower ()})""" ] ]
|
||||
|
@ -210,13 +192,7 @@ let maintain (users : User list) ctx viewInfo =
|
|||
| [] -> space
|
||||
| _ ->
|
||||
table [ _class "pt-table pt-action-table" ] [
|
||||
thead [] [
|
||||
tr [] [
|
||||
th [] [ locStr s["Actions"] ]
|
||||
th [] [ locStr s["Name"] ]
|
||||
th [] [ locStr s["Admin?"] ]
|
||||
]
|
||||
]
|
||||
tableHeadings s [ "Actions"; "Name"; "Admin?" ]
|
||||
users
|
||||
|> List.map (fun user ->
|
||||
let userId = flatGuid user.userId
|
||||
|
|
|
@ -73,14 +73,14 @@ module Configure =
|
|||
route "es" Handlers.Church.maintain
|
||||
routef "/%O/edit" Handlers.Church.edit
|
||||
]
|
||||
route "/class/logon" (redirectTo true "/small-group/log-on")
|
||||
routef "/error/%s" Handlers.Home.error
|
||||
routef "/language/%s" Handlers.Home.language
|
||||
route "/class/logon" (redirectTo true "/small-group/log-on")
|
||||
routef "/error/%s" Handlers.Home.error
|
||||
routef "/language/%s" Handlers.Home.language
|
||||
subRoute "/legal" [
|
||||
route "/privacy-policy" Handlers.Home.privacyPolicy
|
||||
route "/terms-of-service" Handlers.Home.tos
|
||||
]
|
||||
route "/log-off" Handlers.Home.logOff
|
||||
route "/log-off" Handlers.Home.logOff
|
||||
subRoute "/prayer-request" [
|
||||
route "s" (Handlers.PrayerRequest.maintain true)
|
||||
routef "s/email/%s" Handlers.PrayerRequest.email
|
||||
|
@ -107,7 +107,7 @@ module Configure =
|
|||
route "/members" Handlers.SmallGroup.members
|
||||
route "/preferences" Handlers.SmallGroup.preferences
|
||||
]
|
||||
route "/unauthorized" Handlers.Home.unauthorized
|
||||
route "/unauthorized" Handlers.Home.unauthorized
|
||||
subRoute "/user" [
|
||||
route "s" Handlers.User.maintain
|
||||
routef "/%O/edit" Handlers.User.edit
|
||||
|
@ -116,7 +116,7 @@ module Configure =
|
|||
route "/logon" (redirectTo true "/user/log-on")
|
||||
route "/password" Handlers.User.password
|
||||
]
|
||||
route "/" Handlers.Home.homePage
|
||||
route "/" Handlers.Home.homePage
|
||||
]
|
||||
POST [
|
||||
subRoute "/church" [
|
||||
|
|
|
@ -55,6 +55,7 @@ let currentGroup (ctx : HttpContext) =
|
|||
|
||||
open System
|
||||
open Giraffe
|
||||
open Giraffe.Htmx
|
||||
open PrayerTracker.Cookies
|
||||
open PrayerTracker.ViewModels
|
||||
|
||||
|
@ -82,8 +83,8 @@ let viewInfo (ctx : HttpContext) startTicks =
|
|||
HttpOnly = true))
|
||||
| None -> ()
|
||||
let layout =
|
||||
match ctx.TryGetRequestHeader "X-Target" with
|
||||
| Some hdr when hdr = "#pt-body" -> ContentOnly
|
||||
match ctx.Request.Headers.HxTarget with
|
||||
| Some hdr when hdr = "pt-body" -> ContentOnly
|
||||
| Some _ -> PartialPage
|
||||
| None -> FullPage
|
||||
{ AppViewInfo.fresh with
|
||||
|
|
|
@ -9,7 +9,7 @@ this.PT = {
|
|||
* @param {string} url The URL for the help page.
|
||||
*/
|
||||
showHelp(url) {
|
||||
window.open(url, 'helpWindow', 'height=600px,width=450px,toolbar=0,menubar=0,scrollbars=1')
|
||||
window.open(url, "helpWindow", "height=600px,width=450px,toolbar=0,menubar=0,scrollbars=1")
|
||||
return false
|
||||
},
|
||||
|
||||
|
@ -20,8 +20,8 @@ this.PT = {
|
|||
*/
|
||||
confirmDelete(action, prompt) {
|
||||
if (confirm(prompt)) {
|
||||
let form = document.querySelector('#DeleteForm')
|
||||
form.setAttribute('action', action)
|
||||
let form = document.querySelector("#DeleteForm")
|
||||
form.setAttribute("action", action)
|
||||
form.submit()
|
||||
}
|
||||
return false
|
||||
|
@ -41,7 +41,7 @@ this.PT = {
|
|||
* @param {Function} func The function to run once the DOM content is loaded
|
||||
*/
|
||||
onLoad(func) {
|
||||
document.addEventListener('DOMContentLoaded', func)
|
||||
document.addEventListener("DOMContentLoaded", func)
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -55,7 +55,7 @@ this.PT = {
|
|||
const field1Value = document.getElementById(field1).value
|
||||
const field2Element = document.getElementById(field2)
|
||||
if (field1Value === field2Element.value) {
|
||||
field2Element.setCustomValidity('')
|
||||
field2Element.setCustomValidity("")
|
||||
return true
|
||||
}
|
||||
field2Element.setCustomValidity(errorMsg)
|
||||
|
@ -67,8 +67,8 @@ this.PT = {
|
|||
* @param {HTMLElement} div The div to be shown
|
||||
*/
|
||||
showDiv(div) {
|
||||
if (div.className.indexOf(' pt-shown') === -1) {
|
||||
div.className += ' pt-shown'
|
||||
if (div.className.indexOf(" pt-shown") === -1) {
|
||||
div.className += " pt-shown"
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -77,7 +77,7 @@ this.PT = {
|
|||
* @param {HTMLElement} div The div to be hidden
|
||||
*/
|
||||
hideDiv(div) {
|
||||
div.className = div.className.replace(' pt-shown', '')
|
||||
div.className = div.className.replace(" pt-shown", "")
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -85,7 +85,7 @@ this.PT = {
|
|||
*/
|
||||
initCKEditor() {
|
||||
ClassicEditor
|
||||
.create(document.querySelector('#text'))
|
||||
.create(document.querySelector("#Text"))
|
||||
.catch(console.error)
|
||||
},
|
||||
|
||||
|
@ -101,9 +101,9 @@ this.PT = {
|
|||
* If the interface box is checked, show and require the interface URL field (if not, well... don't)
|
||||
*/
|
||||
checkInterface() {
|
||||
const div = document.getElementById('divInterfaceAddress')
|
||||
const addr = document.getElementById('interfaceAddress')
|
||||
if (document.getElementById('hasInterface').checked) {
|
||||
const div = document.getElementById("divInterfaceAddress")
|
||||
const addr = document.getElementById("InterfaceAddress")
|
||||
if (document.getElementById("HasInterface").checked) {
|
||||
PT.showDiv(div)
|
||||
addr.required = true
|
||||
}
|
||||
|
@ -117,8 +117,8 @@ this.PT = {
|
|||
*/
|
||||
onPageLoad() {
|
||||
PT.church.edit.checkInterface()
|
||||
document.getElementById('hasInterface')
|
||||
.addEventListener('click', PT.church.edit.checkInterface)
|
||||
document.getElementById("HasInterface")
|
||||
.addEventListener("click", PT.church.edit.checkInterface)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -154,23 +154,23 @@ this.PT = {
|
|||
*/
|
||||
onPageLoad() {
|
||||
PT.initCKEditor()
|
||||
const sendNo = document.getElementById('sendN')
|
||||
const catDiv = document.getElementById('divCategory')
|
||||
const catSel = document.getElementById('requestType')
|
||||
const addChk = document.getElementById('addToRequestList')
|
||||
if (sendNo !== 'undefined') {
|
||||
const addDiv = document.getElementById('divAddToList')
|
||||
sendNo.addEventListener('click', () => {
|
||||
const sendNo = document.getElementById("SendToClass_N")
|
||||
const catDiv = document.getElementById("divCategory")
|
||||
const catSel = document.getElementById("RequestType")
|
||||
const addChk = document.getElementById("AddToRequestList")
|
||||
if (sendNo !== "undefined") {
|
||||
const addDiv = document.getElementById("divAddToList")
|
||||
sendNo.addEventListener("click", () => {
|
||||
PT.hideDiv(addDiv)
|
||||
PT.hideDiv(catDiv)
|
||||
catSel.required = false
|
||||
addChk.checked = false
|
||||
})
|
||||
document.getElementById('sendY').addEventListener('click', () => {
|
||||
document.getElementById("SendToClass_Y").addEventListener("click", () => {
|
||||
PT.showDiv(addDiv)
|
||||
})
|
||||
}
|
||||
addChk.addEventListener('click', () => {
|
||||
addChk.addEventListener("click", () => {
|
||||
if (addChk.checked) {
|
||||
PT.showDiv(catDiv)
|
||||
catSel.required = true
|
||||
|
@ -190,11 +190,11 @@ this.PT = {
|
|||
* Determine which field should have the focus
|
||||
*/
|
||||
onPageLoad() {
|
||||
const grp = document.getElementById('SmallGroupId')
|
||||
const grp = document.getElementById("SmallGroupId")
|
||||
if (grp.options[grp.selectedIndex].value === '') {
|
||||
grp.focus()
|
||||
} else {
|
||||
document.getElementById('Password').focus()
|
||||
document.getElementById("Password").focus()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -225,9 +225,9 @@ this.PT = {
|
|||
* Show or hide the class password based on the visibility.
|
||||
*/
|
||||
checkVisibility() {
|
||||
const divPw = document.getElementById('divClassPassword')
|
||||
if (document.getElementById('viz_Public').checked
|
||||
|| document.getElementById('viz_Private').checked) {
|
||||
const divPw = document.getElementById("divClassPassword")
|
||||
if (document.getElementById("Visibility_Public").checked
|
||||
|| document.getElementById("Visibility_Private").checked) {
|
||||
// Disable password
|
||||
PT.hideDiv(divPw)
|
||||
} else {
|
||||
|
@ -239,16 +239,16 @@ this.PT = {
|
|||
* Bind the event handlers
|
||||
*/
|
||||
onPageLoad() {
|
||||
['Public', 'Private', 'Password'].map(typ => {
|
||||
document.getElementById(`viz_${typ}`).addEventListener('click',
|
||||
["Public", "Private", "Password"].map(typ => {
|
||||
document.getElementById(`Visibility_${typ}`).addEventListener("click",
|
||||
PT.smallGroup.preferences.checkVisibility)
|
||||
})
|
||||
PT.smallGroup.preferences.checkVisibility()
|
||||
;['lineColor', 'headingColor'].map(name => {
|
||||
document.getElementById(`${name}Type_Name`).addEventListener('click', () => {
|
||||
;["LineColor", "HeadingColor"].map(name => {
|
||||
document.getElementById(`${name}Type_Name`).addEventListener("click", () => {
|
||||
PT.smallGroup.preferences.toggleType(name)
|
||||
})
|
||||
document.getElementById(`${name}Type_RGB`).addEventListener('click', () => {
|
||||
document.getElementById(`${name}Type_RGB`).addEventListener("click", () => {
|
||||
PT.smallGroup.preferences.toggleType(name)
|
||||
})
|
||||
PT.smallGroup.preferences.toggleType(name)
|
||||
|
@ -270,9 +270,13 @@ this.PT = {
|
|||
*/
|
||||
onPageLoad(isNew) {
|
||||
if (isNew) {
|
||||
PT.requireFields(['password', 'passwordConfirm'])
|
||||
PT.requireFields(["Password", "PasswordConfirm"])
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
htmx.on("htmx:configRequest", function (e) {
|
||||
e.detail.headers["X-Target"] = e.detail.target
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue
Block a user