Search, Paging, and "As of" Date (#10)

Issues Fixed:
* Added request search capability (#2)
* Added pagination to search / inactive request lists (#3)
* Added "as of" date display option for requests (#9)
* Updated documentation to reflect the new options and their behavior

Also Fixed (w/o issue numbers):
* Fixed a verbiage error with the confirmation prompts
* Split the I18N for the maintain requests page into its own localized view
* Modified many "magic strings" in the code to use F# discriminated unions instead (stored as single-character codes in the database)
This commit was merged in pull request #10.
This commit is contained in:
Daniel J. Summers
2019-03-20 19:19:02 -05:00
committed by GitHub
parent 6a6b403216
commit 43b6b6d8e0
28 changed files with 1176 additions and 356 deletions

View File

@@ -10,37 +10,40 @@ open System
/// Helper module to return localized reference lists
module ReferenceList =
/// A localized list of the AsOfDateDisplay DU cases
let asOfDateList (s : IStringLocalizer) =
[ NoDisplay.code, s.["Do not display the “as of” date"]
ShortDate.code, s.["Display a short “as of” date"]
LongDate.code, s.["Display a full “as of” date"]
]
/// A list of e-mail type options
let emailTypeList def (s : IStringLocalizer) =
// Localize the default type
let defaultType =
match def with
| EmailType.Html -> s.["HTML Format"].Value
| EmailType.PlainText -> s.["Plain-Text Format"].Value
| EmailType.AttachedPdf -> s.["Attached PDF"].Value
| _ -> ""
| HtmlFormat -> s.["HTML Format"].Value
| PlainTextFormat -> s.["Plain-Text Format"].Value
seq {
yield "", LocalizedString ("", sprintf "%s (%s)" s.["Group Default"].Value defaultType)
yield EmailType.Html, s.["HTML Format"]
yield EmailType.PlainText, s.["Plain-Text Format"]
yield HtmlFormat.code, s.["HTML Format"]
yield PlainTextFormat.code, s.["Plain-Text Format"]
}
/// A list of expiration options
let expirationList (s : IStringLocalizer) includeExpireNow =
seq {
yield "N", s.["Expire Normally"]
yield "Y", s.["Request Never Expires"]
match includeExpireNow with true -> yield "X", s.["Expire Immediately"] | false -> ()
}
|> List.ofSeq
[ yield Automatic.code, s.["Expire Normally"]
yield Manual.code, s.["Request Never Expires"]
match includeExpireNow with true -> yield Forced.code, s.["Expire Immediately"] | false -> ()
]
/// A list of request types
let requestTypeList (s : IStringLocalizer) =
[ RequestType.Current, s.["Current Requests"]
RequestType.Recurring, s.["Long-Term Requests"]
RequestType.Praise, s.["Praise Reports"]
RequestType.Expecting, s.["Expecting"]
RequestType.Announcement, s.["Announcements"]
[ CurrentRequest, s.["Current Requests"]
LongTermRequest, s.["Long-Term Requests"]
PraiseReport, s.["Praise Reports"]
Expecting, s.["Expecting"]
Announcement, s.["Announcements"]
]
@@ -273,6 +276,10 @@ type EditPreferences =
listVisibility : int
/// The small group password
groupPassword : string option
/// The page size for search / inactive requests
pageSize : int
/// How the as-of date should be displayed
asOfDate : string
}
with
static member fromPreferences (prefs : ListPreferences) =
@@ -280,10 +287,10 @@ with
{ expireDays = prefs.daysToExpire
daysToKeepNew = prefs.daysToKeepNew
longTermUpdateWeeks = prefs.longTermUpdateWeeks
requestSort = prefs.requestSort
requestSort = prefs.requestSort.code
emailFromName = prefs.emailFromName
emailFromAddress = prefs.emailFromAddress
defaultEmailType = prefs.defaultEmailType
defaultEmailType = prefs.defaultEmailType.code
headingLineType = setType prefs.lineColor
headingLineColor = prefs.lineColor
headingTextType = setType prefs.headingColor
@@ -293,6 +300,8 @@ with
listFontSize = prefs.textFontSize
timeZone = prefs.timeZoneId
groupPassword = Some prefs.groupPassword
pageSize = prefs.pageSize
asOfDate = prefs.asOfDateDisplay.code
listVisibility =
match true with
| _ when prefs.isPublic -> RequestVisibility.``public``
@@ -311,10 +320,10 @@ with
daysToExpire = this.expireDays
daysToKeepNew = this.daysToKeepNew
longTermUpdateWeeks = this.longTermUpdateWeeks
requestSort = this.requestSort
requestSort = RequestSort.fromCode this.requestSort
emailFromName = this.emailFromName
emailFromAddress = this.emailFromAddress
defaultEmailType = this.defaultEmailType
defaultEmailType = EmailFormat.fromCode this.defaultEmailType
lineColor = this.headingLineColor
headingColor = this.headingTextColor
listFonts = this.listFonts
@@ -323,6 +332,8 @@ with
timeZoneId = this.timeZone
isPublic = isPublic
groupPassword = grpPw
pageSize = this.pageSize
asOfDateDisplay = AsOfDateDisplay.fromCode this.asOfDate
}
@@ -349,20 +360,20 @@ with
/// An empty instance to use for new requests
static member empty =
{ requestId = Guid.Empty
requestType = ""
requestType = CurrentRequest.code
enteredDate = None
skipDateUpdate = None
requestor = None
expiration = "N"
expiration = Automatic.code
text = ""
}
/// Create an instance from an existing request
static member fromRequest req =
{ EditRequest.empty with
requestId = req.prayerRequestId
requestType = req.requestType
requestType = req.requestType.code
requestor = req.requestor
expiration = match req.doNotExpire with true -> "Y" | false -> "N"
expiration = req.expiration.code
text = req.text
}
/// Is this a new request?
@@ -473,12 +484,37 @@ with
}
/// Items needed to display the request maintenance page
[<NoComparison; NoEquality>]
type MaintainRequests =
{ /// The requests to be displayed
requests : PrayerRequest seq
/// The small group to which the requests belong
smallGroup : SmallGroup
/// Whether only active requests are included
onlyActive : bool option
/// The search term for the requests
searchTerm : string option
/// The page number of the results
pageNbr : int option
}
with
static member empty =
{ requests = Seq.empty
smallGroup = SmallGroup.empty
onlyActive = None
searchTerm = None
pageNbr = None
}
/// Items needed to display the small group overview page
[<NoComparison; NoEquality>]
type Overview =
{ /// The total number of active requests
totalActiveReqs : int
/// The numbers of active requests by category
activeReqsByCat : Map<string, int>
activeReqsByCat : Map<PrayerRequestType, int>
/// A count of all requests
allReqs : int
/// A count of all members
@@ -535,15 +571,16 @@ with
|> Seq.ofList
|> Seq.filter (fun req -> req.requestType = cat)
match this.listGroup.preferences.requestSort with
| "D" -> reqs |> Seq.sortByDescending (fun req -> req.updatedDate)
| _ -> reqs |> Seq.sortBy (fun req -> req.requestor)
| SortByDate -> reqs |> Seq.sortByDescending (fun req -> req.updatedDate)
| SortByRequestor -> reqs |> Seq.sortBy (fun req -> req.requestor)
|> List.ofSeq
/// Is this request new?
member this.isNew (req : PrayerRequest) =
(this.date - req.updatedDate).Days <= this.listGroup.preferences.daysToKeepNew
/// Generate this list as HTML
member this.asHtml (s : IStringLocalizer) =
let prefs = this.listGroup.preferences
let prefs = this.listGroup.preferences
let asOfSize = Math.Round (float prefs.textFontSize * 0.8, 2)
[ match this.showHeader with
| true ->
yield div [ _style (sprintf "text-align:center;font-family:%s" prefs.listFonts) ] [
@@ -590,6 +627,18 @@ with
| Some _ -> ()
| None -> ()
yield rawText req.text
match prefs.asOfDateDisplay with
| NoDisplay -> ()
| ShortDate
| LongDate ->
let dt =
match prefs.asOfDateDisplay with
| ShortDate -> req.updatedDate.ToShortDateString ()
| LongDate -> req.updatedDate.ToLongDateString ()
| _ -> ""
yield i [ _style (sprintf "font-size:%.2fpt" asOfSize) ] [
rawText "&nbsp; ("; str s.["as of"].Value; str " "; str dt; rawText ")"
]
])
|> ul []
yield br []
@@ -618,7 +667,17 @@ with
for req in reqs do
let bullet = match this.isNew req with true -> "+" | false -> "-"
let requestor = match req.requestor with Some r -> sprintf "%s - " r | None -> ""
yield sprintf " %s %s%s" bullet requestor (htmlToPlainText req.text)
yield
match this.listGroup.preferences.asOfDateDisplay with
| NoDisplay -> ""
| _ ->
let dt =
match this.listGroup.preferences.asOfDateDisplay with
| ShortDate -> req.updatedDate.ToShortDateString ()
| LongDate -> req.updatedDate.ToLongDateString ()
| _ -> ""
sprintf " (%s %s)" s.["as of"].Value dt
|> sprintf " %s %s%s%s" bullet requestor (htmlToPlainText req.text)
yield " "
}
|> String.concat "\n"