Version 8 #43
| @ -2,6 +2,7 @@ | ||||
| 
 | ||||
| open Giraffe.ViewEngine | ||||
| open Giraffe.ViewEngine.Accessibility | ||||
| open Giraffe.ViewEngine.Htmx | ||||
| open PrayerTracker | ||||
| open PrayerTracker.Entities | ||||
| open PrayerTracker.ViewModels | ||||
| @ -58,71 +59,49 @@ let edit (model : EditChurch) ctx viewInfo = | ||||
| 
 | ||||
| /// View for church maintenance page | ||||
| let maintain (churches : Church list) (stats : Map<string, ChurchStats>) ctx viewInfo = | ||||
|     let s     = I18N.localizer.Force () | ||||
|     let vi = | ||||
|         AppViewInfo.withScopedStyles [ | ||||
|             "#churchList { grid-template-columns: repeat(7, auto); }" | ||||
|         ] viewInfo | ||||
|     let chTbl = | ||||
|     let s  = I18N.localizer.Force () | ||||
|     let vi = AppViewInfo.withScopedStyles [ "#churchList { grid-template-columns: repeat(7, auto); }" ] viewInfo | ||||
|     let churchTable = | ||||
|         match churches with | ||||
|         | [] -> space | ||||
|         | _ -> | ||||
|             section [ _id "churchList"; _class "pt-data-list"; _ariaLabel "Church list" ] [ | ||||
|                 header [] [ locStr s["Actions"] ] | ||||
|                 header [] [ locStr s["Name"] ] | ||||
|                 header [] [ locStr s["Location"] ] | ||||
|                 header [] [ locStr s["Groups"] ] | ||||
|                 header [] [ locStr s["Requests"] ] | ||||
|                 header [] [ locStr s["Users"] ] | ||||
|                 header [] [ locStr s["Interface?"] ] | ||||
|             section [ _id "churchList"; _class "pt-table"; _ariaLabel "Church list" ] [ | ||||
|                 div [ _class "row head" ] [ | ||||
|                     header [ _class "cell" ] [ locStr s["Actions"] ] | ||||
|                     header [ _class "cell" ] [ locStr s["Name"] ] | ||||
|                     header [ _class "cell" ] [ locStr s["Location"] ] | ||||
|                     header [ _class "cell" ] [ locStr s["Groups"] ] | ||||
|                     header [ _class "cell" ] [ locStr s["Requests"] ] | ||||
|                     header [ _class "cell" ] [ locStr s["Users"] ] | ||||
|                     header [ _class "cell" ] [ locStr s["Interface?"] ] | ||||
|                 ] | ||||
|                 for church in churches do | ||||
|                     let churchId  = shortGuid church.Id.Value | ||||
|                     let delAction = $"/church/{churchId}/delete" | ||||
|                     let delPrompt = s["Are you sure you want to delete this {0}?  This action cannot be undone.", | ||||
|                                       $"""{s["Church"].Value.ToLower ()} ({church.Name})"""] | ||||
|                     div [ _class "row" ] [ | ||||
|                         div [] [ | ||||
|                             a [ _href $"/church/{churchId}/edit"; _title s["Edit This Church"].Value ] [ icon "edit" ] | ||||
|                             a [ _href    delAction | ||||
|                                 _title   s["Delete This Church"].Value | ||||
|                                 _onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ] [ | ||||
|                                 icon "delete_forever" | ||||
|                         div [ _class "cell actions" ] [ | ||||
|                             a [ _href $"/church/{churchId}/edit"; _title s["Edit This Church"].Value ] [ | ||||
|                                 iconSized 18 "edit" | ||||
|                             ] | ||||
|                             a [ _href      delAction | ||||
|                                 _title     s["Delete This Church"].Value | ||||
|                                 _hxPost    delAction | ||||
|                                 _hxConfirm delPrompt.Value ] [ | ||||
|                                 iconSized 18 "delete_forever" | ||||
|                             ] | ||||
|                         ] | ||||
|                         div [] [ str church.Name ] | ||||
|                         div [] [ str church.City; rawText ", "; str church.State ] | ||||
|                         div [ _class "pt-right-text" ] [ rawText (stats[churchId].SmallGroups.ToString "N0") ] | ||||
|                         div [ _class "pt-right-text" ] [ rawText (stats[churchId].PrayerRequests.ToString "N0") ] | ||||
|                         div [ _class "pt-right-text" ] [ rawText (stats[churchId].Users.ToString "N0") ] | ||||
|                         div [ _class "pt-center-text" ] [ locStr s[if church.HasVpsInterface then "Yes" else "No"] ] | ||||
|                         div [ _class "cell" ] [ str church.Name ] | ||||
|                         div [ _class "cell" ] [ str church.City; rawText ", "; str church.State ] | ||||
|                         div [ _class "cell pt-right-text" ] [ rawText (stats[churchId].SmallGroups.ToString "N0") ] | ||||
|                         div [ _class "cell pt-right-text" ] [ rawText (stats[churchId].PrayerRequests.ToString "N0") ] | ||||
|                         div [ _class "cell pt-right-text" ] [ rawText (stats[churchId].Users.ToString "N0") ] | ||||
|                         div [ _class "cell pt-center-text" ] [ | ||||
|                             locStr s[if church.HasVpsInterface then "Yes" else "No"] | ||||
|                         ] | ||||
|                     ] | ||||
|             ] | ||||
|             // table [ _class "pt-table pt-action-table" ] [ | ||||
|             //     tableHeadings s [ "Actions"; "Name"; "Location"; "Groups"; "Requests"; "Users"; "Interface?" ] | ||||
|             //     churches | ||||
|             //     |> List.map (fun ch -> | ||||
|             //         let chId      = shortGuid ch.Id.Value | ||||
|             //         let delAction = $"/church/{chId}/delete" | ||||
|             //         let delPrompt = s["Are you sure you want to delete this {0}?  This action cannot be undone.", | ||||
|             //                           $"""{s["Church"].Value.ToLower ()} ({ch.Name})"""] | ||||
|             //         tr [] [ | ||||
|             //             td [] [ | ||||
|             //                 a [ _href $"/church/{chId}/edit"; _title s["Edit This Church"].Value ] [ icon "edit" ] | ||||
|             //                 a [ _href    delAction | ||||
|             //                     _title   s["Delete This Church"].Value | ||||
|             //                     _onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ] [ | ||||
|             //                     icon "delete_forever" | ||||
|             //                 ] | ||||
|             //             ] | ||||
|             //             td [] [ str ch.Name ] | ||||
|             //             td [] [ str ch.City; rawText ", "; str ch.State ] | ||||
|             //             td [ _class "pt-right-text" ] [ rawText (stats[chId].SmallGroups.ToString "N0") ] | ||||
|             //             td [ _class "pt-right-text" ] [ rawText (stats[chId].PrayerRequests.ToString "N0") ] | ||||
|             //             td [ _class "pt-right-text" ] [ rawText (stats[chId].Users.ToString "N0") ] | ||||
|             //             td [ _class "pt-center-text" ] [ locStr s[if ch.HasVpsInterface then "Yes" else "No"] ] | ||||
|             //         ]) | ||||
|             //     |> tbody [] | ||||
|             // ] | ||||
|     [   div [ _class "pt-center-text" ] [ | ||||
|             br [] | ||||
|             a [ _href $"/church/{emptyGuid}/edit"; _title s["Add a New Church"].Value ] [ | ||||
| @ -132,8 +111,10 @@ let maintain (churches : Church list) (stats : Map<string, ChurchStats>) ctx vie | ||||
|             br [] | ||||
|         ] | ||||
|         tableSummary churches.Length s | ||||
|         chTbl | ||||
|         form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ] | ||||
|         form [ _method "post" ] [ | ||||
|             csrfToken ctx | ||||
|             churchTable | ||||
|         ] | ||||
|     ] | ||||
|     |> Layout.Content.wide | ||||
|     |> Layout.standard vi "Maintain Churches" | ||||
|  | ||||
| @ -141,15 +141,15 @@ module Navigation = | ||||
|         let s = I18N.localizer.Force () | ||||
|         header [ _id "pt-language"; Target.body ] [ | ||||
|             div [] [ | ||||
|                 span [ _class "u" ] [ locStr s["Language"]; rawText ": " ] | ||||
|                 span [ _title s["Language"].Value ] [ icon "record_voice_over"; space ] | ||||
|                 match langCode () with | ||||
|                 | "es" ->  | ||||
|                     locStr s["Spanish"] | ||||
|                     rawText "   •   " | ||||
|                     strong [] [ locStr s["Spanish"] ] | ||||
|                     rawText "     " | ||||
|                     a [ _href "/language/en" ] [ locStr s["Change to English"] ] | ||||
|                 | _ -> | ||||
|                     locStr s["English"] | ||||
|                     rawText "   •   " | ||||
|                     strong [] [ locStr s["English"] ] | ||||
|                     rawText "     " | ||||
|                     a [ _href "/language/es" ] [ locStr s["Cambie a Español"] ] | ||||
|             ] | ||||
|             match m.Group with | ||||
|  | ||||
| @ -4,6 +4,8 @@ open System | ||||
| open System.IO | ||||
| open Giraffe | ||||
| open Giraffe.ViewEngine | ||||
| open Giraffe.ViewEngine.Accessibility | ||||
| open Giraffe.ViewEngine.Htmx | ||||
| open Microsoft.AspNetCore.Http | ||||
| open NodaTime | ||||
| open PrayerTracker | ||||
| @ -156,10 +158,11 @@ let maintain (model : MaintainRequests) (ctx : HttpContext) viewInfo = | ||||
|     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 "" | ||||
|         if req.UpdateRequired now prefs.DaysToExpire prefs.LongTermUpdateWeeks then "cell pt-request-update" else "cell" | ||||
|         |> _class  | ||||
|     let reqExp (req : PrayerRequest) = | ||||
|         _class (if req.IsExpired now prefs.DaysToExpire then "pt-request-expired" else "") | ||||
|         _class (if req.IsExpired now prefs.DaysToExpire then "cell pt-request-expired" else "cell") | ||||
|     let vi = AppViewInfo.withScopedStyles [ "#requestList { grid-template-columns: repeat(5, auto); }" ] viewInfo | ||||
|     /// Iterate the sequence once, before we render, so we can get the count of it at the top of the table | ||||
|     let requests = | ||||
|         model.Requests | ||||
| @ -175,33 +178,34 @@ let maintain (model : MaintainRequests) (ctx : HttpContext) viewInfo = | ||||
|                         .Value | ||||
|                 ] | ||||
|                 |> String.concat "" | ||||
|             tr [] [ | ||||
|                 td [] [ | ||||
|             div [ _class "row" ] [ | ||||
|                 div [ _class "cell actions" ] [ | ||||
|                     a [ _href $"/prayer-request/{reqId}/edit"; _title l["Edit This Prayer Request"].Value ] [ | ||||
|                         icon "edit" | ||||
|                         iconSized 18 "edit" | ||||
|                     ] | ||||
|                     if req.IsExpired now prefs.DaysToExpire then | ||||
|                         a [ _href  $"/prayer-request/{reqId}/restore" | ||||
|                             _title l["Restore This Inactive Request"].Value ] [ | ||||
|                             icon "visibility" | ||||
|                             iconSized 18 "visibility" | ||||
|                         ] | ||||
|                     else | ||||
|                         a [ _href  $"/prayer-request/{reqId}/expire" | ||||
|                             _title l["Expire This Request Immediately"].Value ] [ | ||||
|                             icon "visibility_off" | ||||
|                             iconSized 18 "visibility_off" | ||||
|                         ] | ||||
|                     a [ _href    delAction | ||||
|                         _title   l["Delete This Request"].Value | ||||
|                         _onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ] [ | ||||
|                         icon "delete_forever" | ||||
|                     a [ _href      delAction | ||||
|                         _title     l["Delete This Request"].Value | ||||
|                         _hxPost    delAction | ||||
|                         _hxConfirm delPrompt ] [ | ||||
|                         iconSized 18 "delete_forever" | ||||
|                     ] | ||||
|                 ] | ||||
|                 td [ updReq req ] [ | ||||
|                 div [ updReq req ] [ | ||||
|                     str (req.UpdatedDate.ToString(s["MMMM d, yyyy"].Value, Globalization.CultureInfo.CurrentUICulture)) | ||||
|                 ] | ||||
|                 td [] [ locStr types[req.RequestType] ] | ||||
|                 td [ reqExp req ] [ str (match req.Requestor with Some r -> r | None -> " ") ] | ||||
|                 td [] [ | ||||
|                 div [ _class "cell" ] [ locStr types[req.RequestType] ] | ||||
|                 div [ reqExp req ] [ str (match req.Requestor with Some r -> r | None -> " ") ] | ||||
|                 div [ _class "cell" ] [ | ||||
|                     match reqText.Length with | ||||
|                     | len when len < 60 -> rawText reqText | ||||
|                     | _ -> rawText $"{reqText[0..59]}…" | ||||
| @ -235,9 +239,18 @@ let maintain (model : MaintainRequests) (ctx : HttpContext) viewInfo = | ||||
|         match requests.Length with | ||||
|         | 0 -> () | ||||
|         | _ -> | ||||
|             table [ _class "pt-table pt-action-table" ] [ | ||||
|                 tableHeadings s [ "Actions"; "Updated Date"; "Type"; "Requestor"; "Request"] | ||||
|                 tbody [] requests | ||||
|             form [ _method "post" ] [ | ||||
|                 csrfToken ctx | ||||
|                 section [ _id "requestList"; _class "pt-table"; _ariaLabel "Prayer request list" ] [ | ||||
|                     div [ _class "row head" ] [ | ||||
|                         header [ _class "cell" ] [ locStr s["Actions"] ] | ||||
|                         header [ _class "cell" ] [ locStr s["Updated Date"] ] | ||||
|                         header [ _class "cell" ] [ locStr s["Type"] ] | ||||
|                         header [ _class "cell" ] [ locStr s["Requestor"] ] | ||||
|                         header [ _class "cell" ] [ locStr s["Request"] ] | ||||
|                     ] | ||||
|                     yield! requests | ||||
|                 ] | ||||
|             ] | ||||
|         div [ _class "pt-center-text" ] [ | ||||
|             br [] | ||||
| @ -272,10 +285,9 @@ let maintain (model : MaintainRequests) (ctx : HttpContext) viewInfo = | ||||
|                     ] | ||||
|                 | false -> () | ||||
|         ] | ||||
|         form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ] | ||||
|     ] | ||||
|     |> Layout.Content.wide | ||||
|     |> Layout.standard viewInfo (match model.SearchTerm with Some _ -> "Search Results" | None -> "Maintain Requests") | ||||
|     |> Layout.standard vi (match model.SearchTerm with Some _ -> "Search Results" | None -> "Maintain Requests") | ||||
| 
 | ||||
| 
 | ||||
| /// View for the printable prayer request list | ||||
|  | ||||
| @ -1,6 +1,8 @@ | ||||
| module PrayerTracker.Views.SmallGroup | ||||
| 
 | ||||
| open Giraffe.ViewEngine | ||||
| open Giraffe.ViewEngine.Accessibility | ||||
| open Giraffe.ViewEngine.Htmx | ||||
| open Microsoft.Extensions.Localization | ||||
| open PrayerTracker | ||||
| open PrayerTracker.Entities | ||||
| @ -179,33 +181,40 @@ let logOn (groups : SmallGroup list) grpId ctx viewInfo = | ||||
| 
 | ||||
| /// View for the small group maintenance page | ||||
| let maintain (groups : SmallGroup list) ctx viewInfo = | ||||
|     let s      = I18N.localizer.Force () | ||||
|     let grpTbl = | ||||
|     let s  = I18N.localizer.Force () | ||||
|     let vi = AppViewInfo.withScopedStyles [ "#groupList { grid-template-columns: repeat(4, auto); }" ] viewInfo | ||||
|     let groupTable = | ||||
|         match groups with | ||||
|         | [] -> space | ||||
|         | _ -> | ||||
|             table [ _class "pt-table pt-action-table" ] [ | ||||
|                 tableHeadings s [ "Actions"; "Name"; "Church"; "Time Zone"] | ||||
|                 groups | ||||
|                 |> List.map (fun g -> | ||||
|                     let grpId     = shortGuid g.Id.Value | ||||
|             section [ _id "groupList"; _class "pt-table"; _ariaLabel "Small group list" ] [ | ||||
|                 div [ _class "row head" ] [ | ||||
|                     header [ _class "cell" ] [ locStr s["Actions"] ] | ||||
|                     header [ _class "cell" ] [ locStr s["Name"] ] | ||||
|                     header [ _class "cell" ] [ locStr s["Church"] ] | ||||
|                     header [ _class "cell" ] [ locStr s["Time Zone"] ] | ||||
|                 ] | ||||
|                 for group in groups do | ||||
|                     let grpId     = shortGuid group.Id.Value | ||||
|                     let delAction = $"/small-group/{grpId}/delete" | ||||
|                     let delPrompt = s["Are you sure you want to delete this {0}?  This action cannot be undone.", | ||||
|                                          $"""{s["Small Group"].Value.ToLower ()} ({g.Name})""" ].Value | ||||
|                     tr [] [ | ||||
|                         td [] [ | ||||
|                             a [ _href $"/small-group/{grpId}/edit"; _title s["Edit This Group"].Value ] [ icon "edit" ] | ||||
|                             a [ _href    delAction | ||||
|                                 _title   s["Delete This Group"].Value | ||||
|                                 _onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ] [ | ||||
|                                 icon "delete_forever" | ||||
|                                          $"""{s["Small Group"].Value.ToLower ()} ({group.Name})""" ].Value | ||||
|                     div [ _class "row" ] [ | ||||
|                         div [ _class "cell actions" ] [ | ||||
|                             a [ _href $"/small-group/{grpId}/edit"; _title s["Edit This Group"].Value ] [ | ||||
|                                 iconSized 18 "edit" | ||||
|                             ] | ||||
|                             a [ _href      delAction | ||||
|                                 _title     s["Delete This Group"].Value | ||||
|                                 _hxDelete  delAction | ||||
|                                 _hxConfirm delPrompt ] [ | ||||
|                                 iconSized 18 "delete_forever" | ||||
|                             ] | ||||
|                         ] | ||||
|                         td [] [ str g.Name ] | ||||
|                         td [] [ str g.Church.Name ] | ||||
|                         td [] [ locStr (TimeZones.name g.Preferences.TimeZoneId s) ] | ||||
|                     ]) | ||||
|                 |> tbody [] | ||||
|                         div [ _class "cell" ] [ str group.Name ] | ||||
|                         div [ _class "cell" ] [ str group.Church.Name ] | ||||
|                         div [ _class "cell" ] [ locStr (TimeZones.name group.Preferences.TimeZoneId s) ] | ||||
|                     ] | ||||
|             ] | ||||
|     [   div [ _class "pt-center-text" ] [ | ||||
|             br [] | ||||
| @ -216,45 +225,54 @@ let maintain (groups : SmallGroup list) ctx viewInfo = | ||||
|             br [] | ||||
|         ] | ||||
|         tableSummary groups.Length s | ||||
|         grpTbl | ||||
|         form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ] | ||||
|         form [ _method "post" ] [ | ||||
|             csrfToken ctx | ||||
|             groupTable | ||||
|         ] | ||||
|     ] | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard viewInfo "Maintain Groups" | ||||
|     |> Layout.standard vi "Maintain Groups" | ||||
| 
 | ||||
| 
 | ||||
| /// View for the member maintenance page | ||||
| let members (members : Member list) (emailTypes : Map<string, LocalizedString>) ctx viewInfo = | ||||
|     let s      = I18N.localizer.Force () | ||||
|     let mbrTbl = | ||||
|     let s  = I18N.localizer.Force () | ||||
|     let vi = AppViewInfo.withScopedStyles [ "#memberList { grid-template-columns: repeat(4, auto); }" ] viewInfo | ||||
|     let memberTable = | ||||
|         match members with | ||||
|         | [] -> space | ||||
|         | _ -> | ||||
|             table [ _class "pt-table pt-action-table" ] [ | ||||
|                 tableHeadings s [ "Actions"; "Name"; "E-mail Address"; "Format"] | ||||
|                 members | ||||
|                 |> List.map (fun mbr -> | ||||
|             section [ _id "memberList"; _class "pt-table"; _ariaLabel "Small group member list" ] [ | ||||
|                 div [ _class "row head" ] [ | ||||
|                     header [ _class "cell"] [ locStr s["Actions"] ] | ||||
|                     header [ _class "cell"] [ locStr s["Name"] ] | ||||
|                     header [ _class "cell"] [ locStr s["E-mail Address"] ] | ||||
|                     header [ _class "cell"] [ locStr s["Format"] ] | ||||
|                 ] | ||||
|                 for mbr in members do | ||||
|                     let mbrId     = shortGuid mbr.Id.Value | ||||
|                     let delAction = $"/small-group/member/{mbrId}/delete" | ||||
|                     let delPrompt = | ||||
|                         s["Are you sure you want to delete this {0}?  This action cannot be undone.", s["group member"]] | ||||
|                             .Value.Replace("?", $" ({mbr.Name})?") | ||||
|                     tr [] [ | ||||
|                         td [] [ | ||||
|                     div [ _class "row" ] [ | ||||
|                         div [ _class "cell actions" ] [ | ||||
|                             a [ _href $"/small-group/member/{mbrId}/edit"; _title s["Edit This Group Member"].Value ] [ | ||||
|                                 icon "edit" | ||||
|                                 iconSized 18 "edit" | ||||
|                             ] | ||||
|                             a [ _href    delAction | ||||
|                                 _title   s["Delete This Group Member"].Value | ||||
|                                 _onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ] [ | ||||
|                                 icon "delete_forever" | ||||
|                             a [ _href      delAction | ||||
|                                 _title     s["Delete This Group Member"].Value | ||||
|                                 _hxPost    delAction | ||||
|                                 _hxConfirm delPrompt ] [ | ||||
|                                 iconSized 18 "delete_forever" | ||||
|                             ] | ||||
|                         ] | ||||
|                         td [] [ str mbr.Name ] | ||||
|                         td [] [ str mbr.Email ] | ||||
|                         td [] [ locStr emailTypes[defaultArg (mbr.Format |> Option.map EmailFormat.toCode) ""] ] | ||||
|                     ]) | ||||
|                 |> tbody [] | ||||
|                         div [ _class "cell" ] [ str mbr.Name ] | ||||
|                         div [ _class "cell" ] [ str mbr.Email ] | ||||
|                         div [ _class "cell" ] [ | ||||
|                             locStr emailTypes[defaultArg (mbr.Format |> Option.map EmailFormat.toCode) ""] | ||||
|                         ] | ||||
|                     ] | ||||
|             ] | ||||
|     [   div [ _class"pt-center-text" ] [ | ||||
|             br [] | ||||
| @ -265,15 +283,15 @@ let members (members : Member list) (emailTypes : Map<string, LocalizedString>) | ||||
|             br [] | ||||
|         ] | ||||
|         tableSummary members.Length s | ||||
|         mbrTbl | ||||
|         form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ] | ||||
|         form [ _method "post" ] [ | ||||
|             csrfToken ctx | ||||
|             memberTable | ||||
|         ] | ||||
|     ] | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard viewInfo "Maintain Group Members" | ||||
|     |> Layout.standard vi "Maintain Group Members" | ||||
| 
 | ||||
| 
 | ||||
| open Giraffe.ViewEngine.Accessibility | ||||
| 
 | ||||
| /// View for the small group overview page | ||||
| let overview model viewInfo = | ||||
|     let s          = I18N.localizer.Force () | ||||
|  | ||||
| @ -1,6 +1,8 @@ | ||||
| module PrayerTracker.Views.User | ||||
| 
 | ||||
| open Giraffe.ViewEngine | ||||
| open Giraffe.ViewEngine.Accessibility | ||||
| open Giraffe.ViewEngine.Htmx | ||||
| open PrayerTracker | ||||
| open PrayerTracker.ViewModels | ||||
| 
 | ||||
| @ -8,37 +10,33 @@ open PrayerTracker.ViewModels | ||||
| let assignGroups model groups curGroups ctx viewInfo = | ||||
|     let s         = I18N.localizer.Force () | ||||
|     let pageTitle = sprintf "%s • %A" model.UserName s["Assign Groups"] | ||||
|     let vi        = AppViewInfo.withScopedStyles [ "#groupList { grid-template-columns: auto; }" ] viewInfo | ||||
|     form [ _action "/user/small-groups/save"; _method "post"; _class "pt-center-columns"; Target.content ] [ | ||||
|         csrfToken ctx | ||||
|         inputField "hidden" (nameof model.UserId) model.UserId [] | ||||
|         inputField "hidden" (nameof model.UserName) model.UserName [] | ||||
|         table [ _class "pt-table" ] [ | ||||
|             thead [] [ | ||||
|                 tr [] [ | ||||
|                     th [] [ rawText " " ] | ||||
|                     th [] [ locStr s["Group"] ] | ||||
|                 ] | ||||
|         section [ _id "groupList"; _class "pt-table"; _ariaLabel "Assigned small groups" ] [ | ||||
|             div [ _class "row head" ] [ | ||||
|                 header [ _class "cell" ] [ locStr s["Group"] ] | ||||
|             ] | ||||
|             groups | ||||
|             |> List.map (fun (grpId, grpName) -> | ||||
|                 let inputId = $"id-{grpId}" | ||||
|                 tr [] [ | ||||
|                     td [] [ | ||||
|             for groupId, name in groups do | ||||
|                 div [ _class "row" ] [ | ||||
|                     div [ _class "cell" ] [ | ||||
|                         input [ _type  "checkbox" | ||||
|                                 _name  (nameof model.SmallGroups) | ||||
|                                 _id    inputId | ||||
|                                 _value grpId | ||||
|                                 if List.contains grpId curGroups then _checked ] | ||||
|                                 _id    groupId | ||||
|                                 _value groupId | ||||
|                                 if List.contains groupId curGroups then _checked ] | ||||
|                         space | ||||
|                         label [ _for groupId ] [ str name ] | ||||
|                     ] | ||||
|                     td [] [ label [ _for inputId ] [ str grpName ] ] | ||||
|                 ]) | ||||
|             |> tbody [] | ||||
|                 ] | ||||
|         ] | ||||
|         div [ _fieldRow ] [ submit [] "save" s["Save Group Assignments"] ] | ||||
|     ] | ||||
|     |> List.singleton | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard viewInfo pageTitle | ||||
|     |> Layout.standard vi pageTitle | ||||
| 
 | ||||
| 
 | ||||
| /// View for the password change page | ||||
| @ -186,40 +184,45 @@ open PrayerTracker.Entities | ||||
| 
 | ||||
| /// View for the user maintenance page | ||||
| let maintain (users : User list) ctx viewInfo = | ||||
|     let s      = I18N.localizer.Force () | ||||
|     let usrTbl = | ||||
|     let s  = I18N.localizer.Force () | ||||
|     let vi = AppViewInfo.withScopedStyles [ "#userList { grid-template-columns: repeat(4, auto); }" ] viewInfo | ||||
|     let userTable = | ||||
|         match users with | ||||
|         | [] -> space | ||||
|         | _ -> | ||||
|             table [ _class "pt-table pt-action-table" ] [ | ||||
|                 tableHeadings s [ "Actions"; "Name"; "Last Seen"; "Admin?" ] | ||||
|                 users | ||||
|                 |> List.map (fun user -> | ||||
|             section [ _id "userList"; _class "pt-table"; _ariaLabel "User list" ] [ | ||||
|                 div [ _class "row head" ] [ | ||||
|                     header [ _class "cell" ] [ locStr s["Actions"] ] | ||||
|                     header [ _class "cell" ] [ locStr s["Name"] ] | ||||
|                     header [ _class "cell" ] [ locStr s["Last Seen"] ] | ||||
|                     header [ _class "cell" ] [ locStr s["Admin?"] ]   | ||||
|                 ] | ||||
|                 for user in users do | ||||
|                     let userId    = shortGuid user.Id.Value | ||||
|                     let delAction = $"/user/{userId}/delete" | ||||
|                     let delPrompt = s["Are you sure you want to delete this {0}?  This action cannot be undone.", | ||||
|                                       $"""{s["User"].Value.ToLower ()} ({user.Name})"""].Value | ||||
|                     tr [] [ | ||||
|                         td [] [ | ||||
|                             a [ _href $"/user/{userId}/edit"; _title s["Edit This User"].Value ] [ icon "edit" ] | ||||
|                     div [ _class "row" ] [ | ||||
|                         div [ _class "cell actions" ] [ | ||||
|                             a [ _href $"/user/{userId}/edit"; _title s["Edit This User"].Value ] [ iconSized 18 "edit" ] | ||||
|                             a [ _href $"/user/{userId}/small-groups"; _title s["Assign Groups to This User"].Value ] [ | ||||
|                                 icon "group" | ||||
|                                 iconSized 18 "group" | ||||
|                             ] | ||||
|                             a [ _href     delAction | ||||
|                                 _title   s["Delete This User"].Value | ||||
|                                 _onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ] [ | ||||
|                                 icon "delete_forever" | ||||
|                             a [ _href      delAction | ||||
|                                 _title     s["Delete This User"].Value | ||||
|                                 _hxPost    delAction | ||||
|                                 _hxConfirm delPrompt ] [ | ||||
|                                 iconSized 18 "delete_forever" | ||||
|                             ] | ||||
|                         ] | ||||
|                         td [] [ str user.Name ] | ||||
|                         td [] [ | ||||
|                         div [ _class "cell" ] [ str user.Name ] | ||||
|                         div [ _class "cell" ] [ | ||||
|                             str (match user.LastSeen with Some dt -> dt.ToString s["MMMM d, yyyy"] | None -> "--") | ||||
|                         ] | ||||
|                         td [ _class "pt-center-text" ] [ | ||||
|                         div [ _class "cell pt-center-text" ] [ | ||||
|                             if user.IsAdmin then strong [] [ locStr s["Yes"] ] else locStr s["No"] | ||||
|                         ] | ||||
|                     ]) | ||||
|               |> tbody [] | ||||
|                     ] | ||||
|             ] | ||||
|     [   div [ _class "pt-center-text" ] [ | ||||
|             br [] | ||||
| @ -230,8 +233,10 @@ let maintain (users : User list) ctx viewInfo = | ||||
|             br [] | ||||
|         ] | ||||
|         tableSummary users.Length s | ||||
|         usrTbl | ||||
|         form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ] | ||||
|         form [ _method "post" ] [ | ||||
|             csrfToken ctx | ||||
|             userTable | ||||
|         ] | ||||
|     ] | ||||
|     |> Layout.Content.standard | ||||
|     |> Layout.standard viewInfo "Maintain Users" | ||||
|     |> Layout.standard vi "Maintain Users" | ||||
|  | ||||
| @ -263,54 +263,48 @@ footer a:hover { | ||||
|   text-align: right; | ||||
| } | ||||
| 
 | ||||
| .pt-data-list { | ||||
| .pt-table { | ||||
|   display: grid; | ||||
|   justify-content: center; | ||||
|   grid-row-gap: .5rem; | ||||
|   grid-column-gap: 1rem; | ||||
| } | ||||
| .pt-data-list .row { | ||||
| .pt-table .row.head, | ||||
| .pt-table .row { | ||||
|   display: contents; | ||||
| } | ||||
| .pt-data-list .row:hover > * { | ||||
|   background-color: purple; | ||||
| } | ||||
| 
 | ||||
| .pt-table { | ||||
|   margin: auto; | ||||
|   border-collapse: collapse; | ||||
| } | ||||
| .pt-table thead { | ||||
|   background-image: linear-gradient(to bottom, var(--dark), var(--lighter-dark)); | ||||
|   color: white; | ||||
| } | ||||
| .pt-table tbody tr:hover { | ||||
| .pt-table .row:hover > * { | ||||
|   background-color: #eee; | ||||
| } | ||||
| .pt-table tr th, | ||||
| .pt-table tr td { | ||||
| .pt-table .row.head .cell { | ||||
|   background-image: linear-gradient(to bottom, var(--dark), var(--lighter-dark)); | ||||
|   font-weight: bold; | ||||
|   color: white; | ||||
|   text-align: center; | ||||
|   font-size: .85rem; | ||||
| } | ||||
| .pt-table .row.head .cell:first-of-type { | ||||
|   border-top-left-radius: .5rem; | ||||
| } | ||||
| .pt-table .row.head .cell:last-of-type { | ||||
|   border-top-right-radius: .5rem; | ||||
| } | ||||
| .pt-table .cell { | ||||
|   padding: .25rem .5rem; | ||||
|   border-bottom: dotted 1px var(--lighter-dark); | ||||
| } | ||||
| .pt-table tbody tr td { | ||||
|   border-bottom: solid 1px var(--lighter-dark); | ||||
| .pt-table .cell.actions { | ||||
|   border-bottom-color: var(--background); | ||||
| } | ||||
| .pt-action-table tbody tr td:first-child { | ||||
|   white-space: nowrap; | ||||
|   border-bottom: 0; | ||||
| } | ||||
| .pt-action-table tbody tr td:first-child a { | ||||
| .pt-table .cell.actions a { | ||||
|   background-color: gray; | ||||
|   color: white; | ||||
|   border-radius: .5rem; | ||||
|   padding: .125rem .5rem .5rem .5rem; | ||||
|   padding: 0 .5rem .25rem; | ||||
|   margin: 0 .125rem; | ||||
| } | ||||
| .pt-action-table tbody tr:hover td:first-child a { | ||||
| .pt-table .row:hover .cell.actions a { | ||||
|   background-color: var(--dark); | ||||
| } | ||||
| .pt-action-table tbody tr td:first-child a:hover { | ||||
|   border-bottom: 0; | ||||
| } | ||||
| 
 | ||||
| /* TODO: Figure out nice CSS transitions for these; these don't work */ | ||||
| .pt-fadeable { | ||||
|   height: 0; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user