diff --git a/src/PrayerTracker.UI/CommonFunctions.fs b/src/PrayerTracker.UI/CommonFunctions.fs
index 991c85f..dd192bc 100644
--- a/src/PrayerTracker.UI/CommonFunctions.fs
+++ b/src/PrayerTracker.UI/CommonFunctions.fs
@@ -150,6 +150,9 @@ let _inputField = _inputFieldWith []
/// The class that designates a checkbox / label pair
let _checkboxField = _class "pt-checkbox-field"
+/// A group of related fields, inputs, links, etc., displayed in a row
+let _group = _class "pt-group"
+
/// 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
diff --git a/src/PrayerTracker.UI/PrayerRequest.fs b/src/PrayerTracker.UI/PrayerRequest.fs
index 77bac90..4e2b9de 100644
--- a/src/PrayerTracker.UI/PrayerRequest.fs
+++ b/src/PrayerTracker.UI/PrayerRequest.fs
@@ -54,7 +54,7 @@ let edit (model : EditRequest) today ctx viewInfo =
div [ _fieldRow ] [
div [ _inputField ] [
label [] [ locStr s["Expiration"] ]
- span [ _class "pt-radio-group" ] [
+ span [ _group ] [
for code, name in ReferenceList.expirationList s (not model.IsNew) do
label [] [ radio (nameof model.Expiration) "" code model.Expiration; locStr name ]
]
@@ -212,22 +212,22 @@ let maintain (model : MaintainRequests) (ctx : HttpContext) viewInfo =
]
])
|> List.ofSeq
- [ div [ _class "pt-center-text" ] [
- br []
- a [ _href $"/prayer-request/{emptyGuid}/edit"; _title s["Add a New Request"].Value ] [
- icon "add_circle"; rawText " "; locStr s["Add a New Request"]
- ]
- rawText " "
- a [ _href "/prayer-requests/view"; _title s["View Prayer Request List"].Value ] [
- icon "list"; rawText " "; locStr s["View Prayer Request List"]
- ]
- match model.SearchTerm with
- | Some _ ->
- rawText " "
- a [ _href "/prayer-requests"; _title l["Clear Search Criteria"].Value ] [
- icon "highlight_off"; rawText " "; raw l["Clear Search Criteria"]
+ [ br []
+ div [ _fieldRow ] [
+ span [ _group ] [
+ a [ _href $"/prayer-request/{emptyGuid}/edit"; _title s["Add a New Request"].Value ] [
+ icon "add_circle"; rawText " "; locStr s["Add a New Request"]
]
- | None -> ()
+ a [ _href "/prayer-requests/view"; _title s["View Prayer Request List"].Value ] [
+ icon "list"; rawText " "; locStr s["View Prayer Request List"]
+ ]
+ match model.SearchTerm with
+ | Some _ ->
+ a [ _href "/prayer-requests"; _title l["Clear Search Criteria"].Value ] [
+ icon "highlight_off"; rawText " "; raw l["Clear Search Criteria"]
+ ]
+ | None -> ()
+ ]
]
form [ _action "/prayer-requests"; _method "get"; _class "pt-center-text pt-search-form"; Target.content ] [
inputField "text" "search" (defaultArg model.SearchTerm "") [ _placeholder l["Search requests..."].Value ]
@@ -300,9 +300,9 @@ let print model version =
br []
hr []
div [ _style $"font-size:70%%;font-family:{model.SmallGroup.Preferences.FontStack};" ] [
- img [ _src $"""/img/{s["footer_en"].Value}.png"""
+ img [ _src $"""/img/{s["footer_en"].Value}.png"""
_style "vertical-align:text-bottom;"
- _alt imgAlt
+ _alt imgAlt
_title imgAlt ]
space
str version
@@ -315,38 +315,36 @@ let print model version =
let view model viewInfo =
let s = I18N.localizer.Force ()
let pageTitle = $"""{s["Prayer Requests"].Value} • {model.SmallGroup.Name}"""
- let spacer = rawText " "
let dtString = model.Date.ToString ("yyyy-MM-dd", CultureInfo.InvariantCulture)
- [ 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 model.CanEmail then
- spacer
- if model.Date.DayOfWeek <> IsoDayOfWeek.Sunday then
- let rec findSunday (date : LocalDate) =
- if date.DayOfWeek = IsoDayOfWeek.Sunday then date else findSunday (date.PlusDays 1)
- let sunday = findSunday model.Date
- a [ _class "pt-icon-link"
- _href $"""/prayer-requests/view/{sunday.ToString ("yyyy-MM-dd", CultureInfo.InvariantCulture)}"""
- _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"]
+ [ br []
+ div [ _fieldRow ] [
+ span [ _group ] [
+ a [ _class "pt-icon-link"
+ _href $"/prayer-requests/print/{dtString}"
+ _target "_blank"
+ _title s["View Printable"].Value ] [
+ icon "print"; rawText " "; locStr s["View Printable"]
]
- spacer
- a [ _class "pt-icon-link"; _href "/prayer-requests"; _title s["Maintain Prayer Requests"].Value ] [
- icon "compare_arrows"; rawText " "; locStr s["Maintain Prayer Requests"]
+ if model.CanEmail then
+ if model.Date.DayOfWeek <> IsoDayOfWeek.Sunday then
+ let rec findSunday (date : LocalDate) =
+ if date.DayOfWeek = IsoDayOfWeek.Sunday then date else findSunday (date.PlusDays 1)
+ let sunday = findSunday model.Date
+ a [ _class "pt-icon-link"
+ _href $"""/prayer-requests/view/{sunday.ToString ("yyyy-MM-dd", CultureInfo.InvariantCulture)}"""
+ _title s["List for Next Sunday"].Value ] [
+ icon "update"; rawText " "; locStr s["List for Next Sunday"]
+ ]
+ 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"]
+ ]
+ a [ _class "pt-icon-link"; _href "/prayer-requests"; _title s["Maintain Prayer Requests"].Value ] [
+ icon "compare_arrows"; rawText " "; locStr s["Maintain Prayer Requests"]
+ ]
]
]
br []
diff --git a/src/PrayerTracker.UI/SmallGroup.fs b/src/PrayerTracker.UI/SmallGroup.fs
index 74acb79..03e2817 100644
--- a/src/PrayerTracker.UI/SmallGroup.fs
+++ b/src/PrayerTracker.UI/SmallGroup.fs
@@ -30,7 +30,7 @@ let announcement isAdmin ctx viewInfo =
div [ _fieldRow ] [
div [ _inputField ] [
label [] [ locStr s["Send Announcement to"]; rawText ":" ]
- div [ _class "pt-radio-group" ] [
+ div [ _group ] [
label [] [
radio (nameof model.SendToClass) $"{nameof model.SendToClass}_Y" "Y" "Y"
locStr s["This Group"]
@@ -457,7 +457,7 @@ let preferences (model : EditPreferences) ctx viewInfo =
div [ _fieldRow ] [
div [ _inputField ] [
label [] [ locStr s["Color of Heading Lines"] ]
- span [ _class "pt-radio-group" ] [
+ span [ _group ] [
span [] [
label [] [
radio (nameof model.LineColorType) $"{nameof model.LineColorType}_Name" "Name"
@@ -488,7 +488,7 @@ let preferences (model : EditPreferences) ctx viewInfo =
div [ _fieldRow ] [
div [ _inputField ] [
label [] [ locStr s["Color of Heading Text"] ]
- span [ _class "pt-radio-group" ] [
+ span [ _group ] [
span [] [
label [] [
radio (nameof model.HeadingColorType) $"{nameof model.HeadingColorType}_Name" "Name"
@@ -522,14 +522,14 @@ let preferences (model : EditPreferences) ctx viewInfo =
div [ _inputField ] [
label [ _for (nameof model.Fonts) ] [ locStr s["Fonts** for List"] ]
let value = if model.IsNative then "True" else "False"
- span [ _class "pt-radio-group" ] [
+ span [ _group ] [
label [] [
radio (nameof model.IsNative) $"{nameof model.IsNative}_Y" "True" value
locStr s["Native Fonts"]
]
inputField "text" "nativeFontSpacer" "" [ _style "visibility:hidden" ]
]
- span [ _class "pt-radio-group" ] [
+ span [ _group ] [
label [] [
radio (nameof model.IsNative) $"{nameof model.IsNative}_N" "False" value
locStr s["Named Fonts"]
@@ -557,7 +557,7 @@ let preferences (model : EditPreferences) ctx viewInfo =
div [ _fieldRow ] [
div [ _inputField ] [
label [] [ locStr s["Request List Visibility"] ]
- span [ _class "pt-radio-group" ] [
+ span [ _group ] [
let name = nameof model.Visibility
let value = string model.Visibility
label [] [
diff --git a/src/PrayerTracker/PrayerRequest.fs b/src/PrayerTracker/PrayerRequest.fs
index 52a90f0..e577190 100644
--- a/src/PrayerTracker/PrayerRequest.fs
+++ b/src/PrayerTracker/PrayerRequest.fs
@@ -126,7 +126,7 @@ let expire reqId : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task
/// GET /prayer-requests/[group-id]/list
let list groupId : HttpHandler = requireAccess [ AccessLevel.Public ] >=> fun next ctx -> task {
- match! SmallGroups.tryByIdWithPreferences groupId ctx.Conn with
+ match! SmallGroups.tryByIdWithPreferences (SmallGroupId groupId) ctx.Conn with
| Some group when group.Preferences.IsPublic ->
let! reqs =
PrayerRequests.forGroup
diff --git a/src/PrayerTracker/wwwroot/css/app.css b/src/PrayerTracker/wwwroot/css/app.css
index 080a54e..48bbda1 100644
--- a/src/PrayerTracker/wwwroot/css/app.css
+++ b/src/PrayerTracker/wwwroot/css/app.css
@@ -254,7 +254,7 @@ footer a:hover {
text-transform: uppercase;
color: #777;
}
-.pt-radio-group {
+.pt-group {
display: flex;
flex-flow: row;
gap: 1.5rem;
diff --git a/src/PrayerTracker/wwwroot/js/app.js b/src/PrayerTracker/wwwroot/js/app.js
index efd2e43..9b0de67 100644
--- a/src/PrayerTracker/wwwroot/js/app.js
+++ b/src/PrayerTracker/wwwroot/js/app.js
@@ -301,3 +301,23 @@ this.PT = {
htmx.on("htmx:configRequest", function (e) {
e.detail.headers["X-Target"] = e.detail.target
})
+htmx.on("htmx:responseError", function (e) {
+ /** @type {XMLHttpRequest} */
+ const xhr = e.detail.xhr
+
+ const detail = document.createElement("div")
+ detail.className = "description"
+ detail.innerHTML = `(Status code ${xhr.status}: ${xhr.statusText})`
+
+ const msg = document.createElement("div")
+ msg.className = "pt-msg error"
+ msg.innerHTML = `ERROR » ${xhr.responseText}
`
+ msg.appendChild(detail)
+
+ const messages = document.createElement("div")
+ messages.className = "pt-messages"
+ messages.appendChild(msg)
+
+ const title = document.getElementById("pt-page-title")
+ title.parentNode.insertBefore(messages, title.nextSibling)
+})