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

Merged
danieljsummers merged 15 commits from search into master 2019-03-21 00:19:02 +00:00
15 changed files with 341 additions and 70 deletions
Showing only changes of commit ef4f82cf9d - Show all commits

View File

@ -90,7 +90,7 @@ type AppDbContext with
} }
/// Get all (or active) requests for a small group as of now or the specified date /// Get all (or active) requests for a small group as of now or the specified date
member this.AllRequestsForSmallGroup (grp : SmallGroup) clock listDate activeOnly : PrayerRequest seq = member this.AllRequestsForSmallGroup (grp : SmallGroup) clock listDate activeOnly pageNbr : PrayerRequest seq =
let theDate = match listDate with Some dt -> dt | _ -> grp.localDateNow clock let theDate = match listDate with Some dt -> dt | _ -> grp.localDateNow clock
upcast ( upcast (
this.PrayerRequests.AsNoTracking().Where(fun pr -> pr.smallGroupId = grp.smallGroupId) this.PrayerRequests.AsNoTracking().Where(fun pr -> pr.smallGroupId = grp.smallGroupId)
@ -104,7 +104,12 @@ type AppDbContext with
|| RequestType.Expecting = pr.requestType) || RequestType.Expecting = pr.requestType)
&& not pr.isManuallyExpired) && not pr.isManuallyExpired)
| query -> query | query -> query
|> reqSort grp.preferences.requestSort) |> reqSort grp.preferences.requestSort
|> function
| query ->
match activeOnly with
| true -> query.Skip 0
| false -> query.Skip((pageNbr - 1) * grp.preferences.pageSize).Take grp.preferences.pageSize)
/// Count prayer requests for the given small group Id /// Count prayer requests for the given small group Id
member this.CountRequestsBySmallGroup gId = member this.CountRequestsBySmallGroup gId =

View File

@ -1,10 +1,9 @@
module PrayerTracker.Entities.EntitiesTests module PrayerTracker.Entities.EntitiesTests
open Expecto open Expecto
open System
open System.Linq
open NodaTime.Testing open NodaTime.Testing
open NodaTime open NodaTime
open System
[<Tests>] [<Tests>]
let churchTests = let churchTests =
@ -45,6 +44,7 @@ let listPreferencesTests =
Expect.isFalse mt.isPublic "The isPublic flag should not have been set" Expect.isFalse mt.isPublic "The isPublic flag should not have been set"
Expect.equal mt.timeZoneId "America/Denver" "The default time zone should have been America/Denver" Expect.equal mt.timeZoneId "America/Denver" "The default time zone should have been America/Denver"
Expect.equal mt.timeZone.timeZoneId "" "The default preferences should have included an empty time zone" Expect.equal mt.timeZone.timeZoneId "" "The default preferences should have included an empty time zone"
Expect.equal mt.pageSize 100 "The default page size should have been 100"
} }
] ]

View File

@ -68,6 +68,21 @@ let htmlToPlainTextTests =
} }
] ]
[<Tests>]
let makeUrlTests =
testList "makeUrl" [
test "returns the URL when there are no parameters" {
Expect.equal (makeUrl "/test" []) "/test" "The URL should not have had any query string parameters added"
}
test "returns the URL with one query string parameter" {
Expect.equal (makeUrl "/test" [ "unit", "true" ]) "/test?unit=true" "The URL was not constructed properly"
}
test "returns the URL with multiple encoded query string parameters" {
let url = makeUrl "/test" [ "space", "a space"; "turkey", "=" ]
Expect.equal url "/test?space=a+space&turkey=%3D" "The URL was not constructed properly"
}
]
[<Tests>] [<Tests>]
let sndAsStringTests = let sndAsStringTests =
testList "sndAsString" [ testList "sndAsString" [

View File

@ -438,6 +438,19 @@ let groupLogOnTests =
} }
] ]
[<Tests>]
let maintainRequestsTests =
testList "MaintainRequests" [
test "empty is as expected" {
let mt = MaintainRequests.empty
Expect.isEmpty mt.requests "The requests for the model should have been empty"
Expect.equal mt.smallGroup.smallGroupId Guid.Empty "The small group should have been an empty one"
Expect.isNone mt.onlyActive "The only active flag should have been None"
Expect.isNone mt.searchTerm "The search term should have been None"
Expect.isNone mt.pageNbr "The page number should have been None"
}
]
[<Tests>] [<Tests>]
let requestListTests = let requestListTests =
testList "RequestList" [ testList "RequestList" [

View File

@ -75,7 +75,7 @@ let maintain (churches : Church list) (stats : Map<string, ChurchStats>) ctx vi
|> List.map (fun ch -> |> List.map (fun ch ->
let chId = flatGuid ch.churchId let chId = flatGuid ch.churchId
let delAction = sprintf "/church/%s/delete" chId let delAction = sprintf "/church/%s/delete" chId
let delPrompt = s.["Are you want to delete this {0}? This action cannot be undone.", let delPrompt = s.["Are you sure you want to delete this {0}? This action cannot be undone.",
sprintf "%s (%s)" (s.["Church"].Value.ToLower ()) ch.name] sprintf "%s (%s)" (s.["Church"].Value.ToLower ()) ch.name]
tr [] [ tr [] [
td [] [ td [] [

View File

@ -160,39 +160,50 @@ let lists (grps : SmallGroup list) vi =
/// View for the prayer request maintenance page /// View for the prayer request maintenance page
let maintain (reqs : PrayerRequest seq) (grp : SmallGroup) onlyActive (ctx : HttpContext) vi = let maintain m (ctx : HttpContext) vi =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let now = grp.localDateNow (ctx.GetService<IClock> ()) let l = I18N.forView "Requests/Maintain"
use sw = new StringWriter ()
let raw = rawLocText sw
let now = m.smallGroup.localDateNow (ctx.GetService<IClock> ())
let typs = ReferenceList.requestTypeList s |> Map.ofList let typs = ReferenceList.requestTypeList s |> Map.ofList
let updReq (req : PrayerRequest) = let updReq (req : PrayerRequest) =
match req.updateRequired now grp.preferences.daysToExpire grp.preferences.longTermUpdateWeeks with match req.updateRequired now m.smallGroup.preferences.daysToExpire m.smallGroup.preferences.longTermUpdateWeeks with
| true -> "pt-request-update" | true -> "pt-request-update"
| false -> "" | false -> ""
|> _class |> _class
let reqExp (req : PrayerRequest) = let reqExp (req : PrayerRequest) =
_class (match req.isExpired now grp.preferences.daysToExpire with true -> "pt-request-expired" | false -> "") _class (match req.isExpired now m.smallGroup.preferences.daysToExpire with true -> "pt-request-expired" | false -> "")
/// Iterate the sequence once, before we render, so we can get the count of it at the top of the table /// Iterate the sequence once, before we render, so we can get the count of it at the top of the table
let requests = let requests =
reqs m.requests
|> Seq.map (fun req -> |> Seq.map (fun req ->
let reqId = flatGuid req.prayerRequestId let reqId = flatGuid req.prayerRequestId
let reqText = Utils.htmlToPlainText req.text let reqText = Utils.htmlToPlainText req.text
let delAction = sprintf "/prayer-request/%s/delete" reqId let delAction = sprintf "/prayer-request/%s/delete" reqId
let delPrompt = s.["Are you want to delete this prayer request? This action cannot be undone.\\n(If the prayer request has been answered, or an event has passed, consider inactivating it instead.)"].Value let delPrompt =
[ s.["Are you sure you want to delete this {0}? This action cannot be undone.",
s.["Prayer Request"].Value.ToLower() ]
.Value
"\\n"
l.["(If the prayer request has been answered, or an event has passed, consider inactivating it instead.)"]
.Value
]
|> String.concat ""
tr [] [ tr [] [
td [] [ td [] [
yield a [ _href (sprintf "/prayer-request/%s/edit" reqId); _title s.["Edit This Prayer Request"].Value ] yield a [ _href (sprintf "/prayer-request/%s/edit" reqId); _title l.["Edit This Prayer Request"].Value ]
[ icon "edit" ] [ icon "edit" ]
match req.isExpired now grp.preferences.daysToExpire with match req.isExpired now m.smallGroup.preferences.daysToExpire with
| true -> | true ->
yield a [ _href (sprintf "/prayer-request/%s/restore" reqId) yield a [ _href (sprintf "/prayer-request/%s/restore" reqId)
_title s.["Restore This Inactive Request"].Value ] _title l.["Restore This Inactive Request"].Value ]
[ icon "visibility" ] [ icon "visibility" ]
| false -> | false ->
yield a [ _href (sprintf "/prayer-request/%s/expire" reqId) yield a [ _href (sprintf "/prayer-request/%s/expire" reqId)
_title s.["Expire This Request Immediately"].Value ] _title l.["Expire This Request Immediately"].Value ]
[ icon "visibility_off" ] [ icon "visibility_off" ]
yield a [ _href delAction; _title s.["Delete This Request"].Value; yield a [ _href delAction; _title l.["Delete This Request"].Value;
_onclick (sprintf "return PT.confirmDelete('%s','%s')" delAction delPrompt) ] _onclick (sprintf "return PT.confirmDelete('%s','%s')" delAction delPrompt) ]
[ icon "delete_forever" ] [ icon "delete_forever" ]
] ]
@ -210,15 +221,25 @@ let maintain (reqs : PrayerRequest seq) (grp : SmallGroup) onlyActive (ctx : Htt
]) ])
|> List.ofSeq |> List.ofSeq
[ yield div [ _class "pt-center-text" ] [ [ yield div [ _class "pt-center-text" ] [
br [] yield br []
a [ _href (sprintf "/prayer-request/%s/edit" emptyGuid); _title s.["Add a New Request"].Value ] yield a [ _href (sprintf "/prayer-request/%s/edit" emptyGuid); _title s.["Add a New Request"].Value ]
[ icon "add_circle"; rawText " &nbsp;"; locStr s.["Add a New Request"] ] [ icon "add_circle"; rawText " &nbsp;"; locStr s.["Add a New Request"] ]
rawText " &nbsp; &nbsp; &nbsp; " yield rawText " &nbsp; &nbsp; &nbsp; "
a [ _href "/prayer-requests/view"; _title s.["View Prayer Request List"].Value ] yield a [ _href "/prayer-requests/view"; _title s.["View Prayer Request List"].Value ]
[ icon "list"; rawText " &nbsp;"; locStr s.["View Prayer Request List"] ] [ icon "list"; rawText " &nbsp;"; locStr s.["View Prayer Request List"] ]
match m.searchTerm with
| Some _ ->
yield rawText " &nbsp; &nbsp; &nbsp; "
yield a [ _href "/prayer-requests"; _title l.["Clear Search Criteria"].Value ]
[ icon "highlight_off"; rawText " &nbsp;"; raw l.["Clear Search Criteria"] ]
| None -> ()
] ]
yield form [ _action "/prayer-requests"; _method "get"; _class "pt-center-text pt-search-form" ] [ yield form [ _action "/prayer-requests"; _method "get"; _class "pt-center-text pt-search-form" ] [
input [ _type "text"; _name "search"; _placeholder s.["Search requests..."].Value ] input [ _type "text"
_name "search"
_placeholder l.["Search requests..."].Value
_value (defaultArg m.searchTerm "")
]
space space
submit [] "search" s.["Search"] submit [] "search" s.["Search"]
] ]
@ -241,20 +262,41 @@ let maintain (reqs : PrayerRequest seq) (grp : SmallGroup) onlyActive (ctx : Htt
] ]
yield div [ _class "pt-center-text" ] [ yield div [ _class "pt-center-text" ] [
yield br [] yield br []
match onlyActive with match m.onlyActive with
| true -> | Some true ->
yield locStr s.["Inactive requests are currently not shown"] yield raw l.["Inactive requests are currently not shown"]
yield br [] yield br []
yield a [ _href "/prayer-requests/inactive" ] [ locStr s.["Show Inactive Requests"] ] yield a [ _href "/prayer-requests/inactive" ] [ raw l.["Show Inactive Requests"] ]
| false -> | _ ->
yield locStr s.["Inactive requests are currently shown"] match Option.isSome m.onlyActive with
yield br [] | true ->
yield a [ _href "/prayer-requests" ] [ locStr s.["Do Not Show Inactive Requests"] ] yield raw l.["Inactive requests are currently shown"]
yield br []
yield a [ _href "/prayer-requests" ] [ raw l.["Do Not Show Inactive Requests"] ]
yield br []
yield br []
| false -> ()
let srch = [ match m.searchTerm with Some s -> yield "search", s | None -> () ]
let url = match m.onlyActive with Some true | None -> "" | _ -> "/inactive" |> sprintf "/prayer-requests%s"
let pg = defaultArg m.pageNbr 1
match pg with
| 1 -> ()
| _ ->
// button (_type "submit" :: attrs) [ icon ico; rawText " &nbsp;"; locStr text ]
let withPage = match pg with 2 -> srch | _ -> ("page", string (pg - 1)) :: srch
yield a [ _href (makeUrl url withPage) ]
[ icon "keyboard_arrow_left"; space; raw l.["Previous Page"] ]
yield rawText " &nbsp; &nbsp; "
match requests.Length = m.smallGroup.preferences.pageSize with
| true ->
yield a [ _href (makeUrl url (("page", string (pg + 1)) :: srch)) ]
[ raw l.["Next Page"]; space; icon "keyboard_arrow_right" ]
| false -> ()
] ]
yield form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ] yield form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ]
] ]
|> Layout.Content.wide |> Layout.Content.wide
|> Layout.standard vi "Maintain Requests" |> Layout.standard vi (match m.searchTerm with Some _ -> "Search Results" | None -> "Maintain Requests")
/// View for the printable prayer request list /// View for the printable prayer request list

View File

@ -55,6 +55,9 @@
<EmbeddedResource Update="Resources\Views\Requests\Lists.es.resx"> <EmbeddedResource Update="Resources\Views\Requests\Lists.es.resx">
<Generator>ResXFileCodeGenerator</Generator> <Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Update="Resources\Views\Requests\Maintain.es.resx">
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\Views\SmallGroup\Preferences.es.resx"> <EmbeddedResource Update="Resources\Views\SmallGroup\Preferences.es.resx">
<Generator>ResXFileCodeGenerator</Generator> <Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource> </EmbeddedResource>

View File

@ -144,8 +144,8 @@
<data name="Aqua" xml:space="preserve"> <data name="Aqua" xml:space="preserve">
<value>Verde Azulado Brillante</value> <value>Verde Azulado Brillante</value>
</data> </data>
<data name="Are you want to delete this {0}? This action cannot be undone." xml:space="preserve"> <data name="Are you sure you want to delete this {0}? This action cannot be undone." xml:space="preserve">
<value>¿Está desea eliminar este {0}? Esta acción no se puede deshacer.</value> <value>¿Seguro que desea eliminar este {0}? Esta acción no se puede deshacer.</value>
</data> </data>
<data name="Attached PDF" xml:space="preserve"> <data name="Attached PDF" xml:space="preserve">
<value>PDF Adjunto</value> <value>PDF Adjunto</value>
@ -612,9 +612,6 @@
<data name="Delete This Group" xml:space="preserve"> <data name="Delete This Group" xml:space="preserve">
<value>Eliminar Este Grupo</value> <value>Eliminar Este Grupo</value>
</data> </data>
<data name="Do Not Show Inactive Requests" xml:space="preserve">
<value>No Muestran las Peticiones Inactivos</value>
</data>
<data name="E-mail" xml:space="preserve"> <data name="E-mail" xml:space="preserve">
<value>Correo Electrónico</value> <value>Correo Electrónico</value>
</data> </data>
@ -633,12 +630,6 @@
<data name="Group Preferences" xml:space="preserve"> <data name="Group Preferences" xml:space="preserve">
<value>Las Preferencias del Grupo</value> <value>Las Preferencias del Grupo</value>
</data> </data>
<data name="Inactive requests are currently not shown" xml:space="preserve">
<value>Peticiones inactivas no se muestra actualmente</value>
</data>
<data name="Inactive requests are currently shown" xml:space="preserve">
<value>Peticiones inactivas se muestra actualmente</value>
</data>
<data name="Maintain Groups" xml:space="preserve"> <data name="Maintain Groups" xml:space="preserve">
<value>Mantener los Grupos</value> <value>Mantener los Grupos</value>
</data> </data>
@ -696,9 +687,6 @@
<data name="Send Announcement to" xml:space="preserve"> <data name="Send Announcement to" xml:space="preserve">
<value>Enviar anuncio a</value> <value>Enviar anuncio a</value>
</data> </data>
<data name="Show Inactive Requests" xml:space="preserve">
<value>Muestran las Peticiones Inactivos</value>
</data>
<data name="Sort by Last Updated Date" xml:space="preserve"> <data name="Sort by Last Updated Date" xml:space="preserve">
<value>Ordenar por Fecha de Última Actualización</value> <value>Ordenar por Fecha de Última Actualización</value>
</data> </data>
@ -729,15 +717,6 @@
<data name="Active Requests" xml:space="preserve"> <data name="Active Requests" xml:space="preserve">
<value>Peticiones Activas</value> <value>Peticiones Activas</value>
</data> </data>
<data name="Delete This Request" xml:space="preserve">
<value>Eliminar esta petición</value>
</data>
<data name="Edit This Prayer Request" xml:space="preserve">
<value>Editar esta petición de oración</value>
</data>
<data name="Expire This Request Immediately" xml:space="preserve">
<value>Expirar esta petición de oración de inmediato</value>
</data>
<data name="Maintain Prayer Requests" xml:space="preserve"> <data name="Maintain Prayer Requests" xml:space="preserve">
<value>Mantener las Peticiones de Oración</value> <value>Mantener las Peticiones de Oración</value>
</data> </data>
@ -747,9 +726,6 @@
<data name="Quick Actions" xml:space="preserve"> <data name="Quick Actions" xml:space="preserve">
<value>Acciones Rápidas</value> <value>Acciones Rápidas</value>
</data> </data>
<data name="Restore This Inactive Request" xml:space="preserve">
<value>Restaurar esta petición inactiva</value>
</data>
<data name="Save" xml:space="preserve"> <data name="Save" xml:space="preserve">
<value>Guardar</value> <value>Guardar</value>
</data> </data>
@ -822,7 +798,10 @@
<data name="Search" xml:space="preserve"> <data name="Search" xml:space="preserve">
<value>Buscar</value> <value>Buscar</value>
</data> </data>
<data name="Search requests..." xml:space="preserve"> <data name="Prayer Request" xml:space="preserve">
<value>Busca las peticiones...</value> <value>Petición de Oración</value>
</data>
<data name="Search Results" xml:space="preserve">
<value>Resultados de la Búsqueda</value>
</data> </data>
</root> </root>

View File

@ -0,0 +1,159 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="(If the prayer request has been answered, or an event has passed, consider inactivating it instead.)" xml:space="preserve">
<value>(Si la solicitud de oración ha sido respondida o si un evento ha pasado, considere desactivarla.)</value>
</data>
<data name="Clear Search Criteria" xml:space="preserve">
<value>Borrar los Criterios de Búsqueda</value>
</data>
<data name="Delete This Request" xml:space="preserve">
<value>Eliminar esta petición</value>
</data>
<data name="Do Not Show Inactive Requests" xml:space="preserve">
<value>No Muestran las Peticiones Inactivos</value>
</data>
<data name="Edit This Prayer Request" xml:space="preserve">
<value>Editar esta petición de oración</value>
</data>
<data name="Expire This Request Immediately" xml:space="preserve">
<value>Expirar esta petición de oración de inmediato</value>
</data>
<data name="Inactive requests are currently not shown" xml:space="preserve">
<value>Peticiones inactivas no se muestra actualmente</value>
</data>
<data name="Inactive requests are currently shown" xml:space="preserve">
<value>Peticiones inactivas se muestra actualmente</value>
</data>
<data name="Next Page" xml:space="preserve">
<value>Siguiente Página</value>
</data>
<data name="Previous Page" xml:space="preserve">
<value>Página Anterior</value>
</data>
<data name="Restore This Inactive Request" xml:space="preserve">
<value>Restaurar esta petición inactiva</value>
</data>
<data name="Search requests..." xml:space="preserve">
<value>Busca las peticiones...</value>
</data>
<data name="Show Inactive Requests" xml:space="preserve">
<value>Muestran las Peticiones Inactivos</value>
</data>
</root>

View File

@ -192,7 +192,7 @@ let maintain (grps : SmallGroup list) ctx vi =
|> List.map (fun g -> |> List.map (fun g ->
let grpId = flatGuid g.smallGroupId let grpId = flatGuid g.smallGroupId
let delAction = sprintf "/small-group/%s/delete" grpId let delAction = sprintf "/small-group/%s/delete" grpId
let delPrompt = s.["Are you want to delete this {0}? This action cannot be undone.", let delPrompt = s.["Are you sure you want to delete this {0}? This action cannot be undone.",
sprintf "%s (%s)" (s.["Small Group"].Value.ToLower ()) g.name].Value sprintf "%s (%s)" (s.["Small Group"].Value.ToLower ()) g.name].Value
tr [] [ tr [] [
td [] [ td [] [
@ -246,8 +246,10 @@ let members (mbrs : Member list) (emailTyps : Map<string, LocalizedString>) ctx
|> List.map (fun mbr -> |> List.map (fun mbr ->
let mbrId = flatGuid mbr.memberId let mbrId = flatGuid mbr.memberId
let delAction = sprintf "/small-group/member/%s/delete" mbrId let delAction = sprintf "/small-group/member/%s/delete" mbrId
let delPrompt = s.["Are you want to delete this {0} ({1})? This action cannot be undone.", let delPrompt =
s.["group member"], mbr.memberName].Value s.["Are you sure you want to delete this {0}? This action cannot be undone.", s.["group member"]]
.Value
.Replace("?", sprintf " (%s)?" mbr.memberName)
tr [] [ tr [] [
td [] [ td [] [
a [ _href (sprintf "/small-group/member/%s/edit" mbrId); _title s.["Edit This Group Member"].Value ] a [ _href (sprintf "/small-group/member/%s/edit" mbrId); _title s.["Edit This Group Member"].Value ]

View File

@ -190,7 +190,7 @@ let maintain (users : User list) ctx vi =
|> List.map (fun user -> |> List.map (fun user ->
let userId = flatGuid user.userId let userId = flatGuid user.userId
let delAction = sprintf "/user/%s/delete" userId let delAction = sprintf "/user/%s/delete" userId
let delPrompt = s.["Are you want to delete this {0}? This action cannot be undone.", let delPrompt = s.["Are you sure you want to delete this {0}? This action cannot be undone.",
(sprintf "%s (%s)" (s.["User"].Value.ToLower()) user.fullName)].Value (sprintf "%s (%s)" (s.["User"].Value.ToLower()) user.fullName)].Value
tr [] [ tr [] [
td [] [ td [] [

View File

@ -127,6 +127,20 @@ let htmlToPlainText html =
/// Get the second portion of a tuple as a string /// Get the second portion of a tuple as a string
let sndAsString x = (snd >> string) x let sndAsString x = (snd >> string) x
/// Make a URL with query string parameters
let makeUrl (url : string) (qs : (string * string) list) =
let queryString =
qs
|> List.fold
(fun (acc : StringBuilder) (key, value) ->
acc.Append(key).Append("=").Append(WebUtility.UrlEncode value).Append "&")
(StringBuilder ())
match queryString.Length with
| 0 -> url
| _ -> queryString.Insert(0, "?").Insert(0, url).Remove(queryString.Length - 1, 1).ToString ()
/// "Magic string" repository /// "Magic string" repository
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module Key = module Key =

View File

@ -473,7 +473,32 @@ 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 /// Items needed to display the small group overview page
[<NoComparison; NoEquality>]
type Overview = type Overview =
{ /// The total number of active requests { /// The total number of active requests
totalActiveReqs : int totalActiveReqs : int

View File

@ -31,7 +31,7 @@ let private generateRequestList ctx date =
match date with match date with
| Some d -> d | Some d -> d
| None -> grp.localDateNow clock | None -> grp.localDateNow clock
let reqs = ctx.dbContext().AllRequestsForSmallGroup grp clock (Some listDate) true let reqs = ctx.dbContext().AllRequestsForSmallGroup grp clock (Some listDate) true 0
{ requests = reqs |> List.ofSeq { requests = reqs |> List.ofSeq
date = listDate date = listDate
listGroup = grp listGroup = grp
@ -155,7 +155,7 @@ let list groupId : HttpHandler =
match grp with match grp with
| Some g when g.preferences.isPublic -> | Some g when g.preferences.isPublic ->
let clock = ctx.GetService<IClock> () let clock = ctx.GetService<IClock> ()
let reqs = db.AllRequestsForSmallGroup g clock None true let reqs = db.AllRequestsForSmallGroup g clock None true 0
return! return!
viewInfo ctx startTicks viewInfo ctx startTicks
|> Views.PrayerRequest.list |> Views.PrayerRequest.list
@ -199,13 +199,27 @@ let maintain onlyActive : HttpHandler =
let db = ctx.dbContext () let db = ctx.dbContext ()
let grp = currentGroup ctx let grp = currentGroup ctx
task { task {
let reqs = let pageNbr =
match ctx.GetQueryStringValue "page" with
| Ok pg -> match Int32.TryParse pg with true, p -> p | false, _ -> 1
| Error _ -> 1
let m =
match ctx.GetQueryStringValue "search" with match ctx.GetQueryStringValue "search" with
| Ok srch -> db.SearchRequestsForSmallGroup grp srch 1 | Ok srch ->
| Error _ -> db.AllRequestsForSmallGroup grp (ctx.GetService<IClock> ()) None onlyActive { MaintainRequests.empty with
requests = db.SearchRequestsForSmallGroup grp srch pageNbr
searchTerm = Some srch
pageNbr = Some pageNbr
}
| Error _ ->
{ MaintainRequests.empty with
requests = db.AllRequestsForSmallGroup grp (ctx.GetService<IClock> ()) None onlyActive pageNbr
onlyActive = Some onlyActive
pageNbr = match onlyActive with true -> None | false -> Some pageNbr
}
return! return!
{ viewInfo ctx startTicks with helpLink = Some Help.maintainRequests } { viewInfo ctx startTicks with helpLink = Some Help.maintainRequests }
|> Views.PrayerRequest.maintain reqs grp onlyActive ctx |> Views.PrayerRequest.maintain { m with smallGroup = grp } ctx
|> renderHtml next ctx |> renderHtml next ctx
} }

View File

@ -208,7 +208,7 @@ let overview : HttpHandler =
let db = ctx.dbContext () let db = ctx.dbContext ()
let clock = ctx.GetService<IClock> () let clock = ctx.GetService<IClock> ()
task { task {
let reqs = db.AllRequestsForSmallGroup (currentGroup ctx) clock None true |> List.ofSeq let reqs = db.AllRequestsForSmallGroup (currentGroup ctx) clock None true 0 |> List.ofSeq
let! reqCount = db.CountRequestsBySmallGroup (currentGroup ctx).smallGroupId let! reqCount = db.CountRequestsBySmallGroup (currentGroup ctx).smallGroupId
let! mbrCount = db.CountMembersForSmallGroup (currentGroup ctx).smallGroupId let! mbrCount = db.CountMembersForSmallGroup (currentGroup ctx).smallGroupId
let m = let m =