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:
committed by
GitHub
parent
6a6b403216
commit
43b6b6d8e0
@@ -75,7 +75,7 @@ let maintain (churches : Church list) (stats : Map<string, ChurchStats>) ctx vi
|
||||
|> List.map (fun ch ->
|
||||
let chId = flatGuid ch.churchId
|
||||
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]
|
||||
tr [] [
|
||||
td [] [
|
||||
|
||||
@@ -23,7 +23,7 @@ let edit (m : EditRequest) today ctx vi =
|
||||
label [ _for "requestType" ] [ locStr s.["Request Type"] ]
|
||||
ReferenceList.requestTypeList s
|
||||
|> Seq.ofList
|
||||
|> Seq.map (fun item -> fst item, (snd item).Value)
|
||||
|> Seq.map (fun (typ, desc) -> typ.code, desc.Value)
|
||||
|> selectList "requestType" m.requestType [ _required; _autofocus ]
|
||||
]
|
||||
yield div [ _class "pt-field" ] [
|
||||
@@ -160,39 +160,50 @@ let lists (grps : SmallGroup list) vi =
|
||||
|
||||
|
||||
/// 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 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 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"
|
||||
| false -> ""
|
||||
|> _class
|
||||
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
|
||||
let requests =
|
||||
reqs
|
||||
m.requests
|
||||
|> Seq.map (fun req ->
|
||||
let reqId = flatGuid req.prayerRequestId
|
||||
let reqText = Utils.htmlToPlainText req.text
|
||||
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 [] [
|
||||
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" ]
|
||||
match req.isExpired now grp.preferences.daysToExpire with
|
||||
match req.isExpired now m.smallGroup.preferences.daysToExpire with
|
||||
| true ->
|
||||
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" ]
|
||||
| false ->
|
||||
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" ]
|
||||
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) ]
|
||||
[ icon "delete_forever" ]
|
||||
]
|
||||
@@ -205,20 +216,34 @@ let maintain (reqs : PrayerRequest seq) (grp : SmallGroup) onlyActive (ctx : Htt
|
||||
yield
|
||||
match 60 > reqText.Length with
|
||||
| true -> rawText reqText
|
||||
| false -> rawText (sprintf "%s…" (reqText.Substring (0, 60)))
|
||||
| false -> rawText (sprintf "%s…" reqText.[0..59])
|
||||
]
|
||||
])
|
||||
|> List.ofSeq
|
||||
[ yield div [ _class "pt-center-text" ] [
|
||||
br []
|
||||
a [ _href (sprintf "/prayer-request/%s/edit" emptyGuid); _title s.["Add a New Request"].Value ]
|
||||
yield br []
|
||||
yield a [ _href (sprintf "/prayer-request/%s/edit" emptyGuid); _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 ]
|
||||
yield rawText " "
|
||||
yield a [ _href "/prayer-requests/view"; _title s.["View Prayer Request List"].Value ]
|
||||
[ icon "list"; rawText " "; locStr s.["View Prayer Request List"] ]
|
||||
br []
|
||||
br []
|
||||
match m.searchTerm with
|
||||
| Some _ ->
|
||||
yield rawText " "
|
||||
yield a [ _href "/prayer-requests"; _title l.["Clear Search Criteria"].Value ]
|
||||
[ icon "highlight_off"; rawText " "; raw l.["Clear Search Criteria"] ]
|
||||
| None -> ()
|
||||
]
|
||||
yield form [ _action "/prayer-requests"; _method "get"; _class "pt-center-text pt-search-form" ] [
|
||||
input [ _type "text"
|
||||
_name "search"
|
||||
_placeholder l.["Search requests..."].Value
|
||||
_value (defaultArg m.searchTerm "")
|
||||
]
|
||||
space
|
||||
submit [] "search" s.["Search"]
|
||||
]
|
||||
yield br []
|
||||
yield tableSummary requests.Length s
|
||||
match requests.Length with
|
||||
| 0 -> ()
|
||||
@@ -237,20 +262,41 @@ let maintain (reqs : PrayerRequest seq) (grp : SmallGroup) onlyActive (ctx : Htt
|
||||
]
|
||||
yield div [ _class "pt-center-text" ] [
|
||||
yield br []
|
||||
match onlyActive with
|
||||
| true ->
|
||||
yield locStr s.["Inactive requests are currently not shown"]
|
||||
match m.onlyActive with
|
||||
| Some true ->
|
||||
yield raw l.["Inactive requests are currently not shown"]
|
||||
yield br []
|
||||
yield a [ _href "/prayer-requests/inactive" ] [ locStr s.["Show Inactive Requests"] ]
|
||||
| false ->
|
||||
yield locStr s.["Inactive requests are currently shown"]
|
||||
yield br []
|
||||
yield a [ _href "/prayer-requests" ] [ locStr s.["Do Not Show Inactive Requests"] ]
|
||||
yield a [ _href "/prayer-requests/inactive" ] [ raw l.["Show Inactive Requests"] ]
|
||||
| _ ->
|
||||
match Option.isSome m.onlyActive with
|
||||
| true ->
|
||||
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 " "; 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 " "
|
||||
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 ]
|
||||
]
|
||||
|> 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
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<AssemblyVersion>7.0.0.0</AssemblyVersion>
|
||||
<AssemblyVersion>7.3.0.0</AssemblyVersion>
|
||||
<FileVersion>7.0.0.0</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -55,6 +55,9 @@
|
||||
<EmbeddedResource Update="Resources\Views\Requests\Lists.es.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="Resources\Views\Requests\Maintain.es.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="Resources\Views\SmallGroup\Preferences.es.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
|
||||
@@ -144,8 +144,8 @@
|
||||
<data name="Aqua" xml:space="preserve">
|
||||
<value>Verde Azulado Brillante</value>
|
||||
</data>
|
||||
<data name="Are 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>
|
||||
<data name="Are you sure you want to delete this {0}? This action cannot be undone." xml:space="preserve">
|
||||
<value>¿Seguro que desea eliminar este {0}? Esta acción no se puede deshacer.</value>
|
||||
</data>
|
||||
<data name="Attached PDF" xml:space="preserve">
|
||||
<value>PDF Adjunto</value>
|
||||
@@ -612,9 +612,6 @@
|
||||
<data name="Delete This Group" xml:space="preserve">
|
||||
<value>Eliminar Este Grupo</value>
|
||||
</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">
|
||||
<value>Correo Electrónico</value>
|
||||
</data>
|
||||
@@ -633,12 +630,6 @@
|
||||
<data name="Group Preferences" xml:space="preserve">
|
||||
<value>Las Preferencias del Grupo</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="Maintain Groups" xml:space="preserve">
|
||||
<value>Mantener los Grupos</value>
|
||||
</data>
|
||||
@@ -696,9 +687,6 @@
|
||||
<data name="Send Announcement to" xml:space="preserve">
|
||||
<value>Enviar anuncio a</value>
|
||||
</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">
|
||||
<value>Ordenar por Fecha de Última Actualización</value>
|
||||
</data>
|
||||
@@ -729,15 +717,6 @@
|
||||
<data name="Active Requests" xml:space="preserve">
|
||||
<value>Peticiones Activas</value>
|
||||
</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">
|
||||
<value>Mantener las Peticiones de Oración</value>
|
||||
</data>
|
||||
@@ -747,9 +726,6 @@
|
||||
<data name="Quick Actions" xml:space="preserve">
|
||||
<value>Acciones Rápidas</value>
|
||||
</data>
|
||||
<data name="Restore This Inactive Request" xml:space="preserve">
|
||||
<value>Restaurar esta petición inactiva</value>
|
||||
</data>
|
||||
<data name="Save" xml:space="preserve">
|
||||
<value>Guardar</value>
|
||||
</data>
|
||||
@@ -819,4 +795,31 @@
|
||||
<data name="Click for Help on This Page" xml:space="preserve">
|
||||
<value>Haga Clic para Obtener Ayuda en Esta Página</value>
|
||||
</data>
|
||||
<data name="Search" xml:space="preserve">
|
||||
<value>Buscar</value>
|
||||
</data>
|
||||
<data name="Display a full “as of” date" xml:space="preserve">
|
||||
<value>Mostrar una fecha “como de” completa</value>
|
||||
</data>
|
||||
<data name="Display a short “as of” date" xml:space="preserve">
|
||||
<value>Mostrar una fecha “como de” corta</value>
|
||||
</data>
|
||||
<data name="Do not display the “as of” date" xml:space="preserve">
|
||||
<value>No se muestran la fecha “como de”</value>
|
||||
</data>
|
||||
<data name="Page Size" xml:space="preserve">
|
||||
<value>Tamaño de Página</value>
|
||||
</data>
|
||||
<data name="Prayer Request" xml:space="preserve">
|
||||
<value>Petición de Oración</value>
|
||||
</data>
|
||||
<data name="Search Results" xml:space="preserve">
|
||||
<value>Resultados de la Búsqueda</value>
|
||||
</data>
|
||||
<data name="“As of” Date Display" xml:space="preserve">
|
||||
<value>Visualización de la Fecha “Como de”</value>
|
||||
</data>
|
||||
<data name="as of" xml:space="preserve">
|
||||
<value>como de</value>
|
||||
</data>
|
||||
</root>
|
||||
159
src/PrayerTracker.UI/Resources/Views/Requests/Maintain.es.resx
Normal file
159
src/PrayerTracker.UI/Resources/Views/Requests/Maintain.es.resx
Normal 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>
|
||||
@@ -45,7 +45,7 @@ let announcement isAdmin ctx vi =
|
||||
label [ _for "requestType" ] [ locStr s.["Request Type"] ]
|
||||
reqTypes
|
||||
|> Seq.ofList
|
||||
|> Seq.map (fun item -> fst item, (snd item).Value)
|
||||
|> Seq.map (fun (typ, desc) -> typ.code, desc.Value)
|
||||
|> selectList "requestType" "Announcement" []
|
||||
]
|
||||
]
|
||||
@@ -192,7 +192,7 @@ let maintain (grps : SmallGroup list) ctx vi =
|
||||
|> List.map (fun g ->
|
||||
let grpId = flatGuid g.smallGroupId
|
||||
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
|
||||
tr [] [
|
||||
td [] [
|
||||
@@ -246,8 +246,10 @@ let members (mbrs : Member list) (emailTyps : Map<string, LocalizedString>) ctx
|
||||
|> List.map (fun mbr ->
|
||||
let mbrId = flatGuid mbr.memberId
|
||||
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.",
|
||||
s.["group member"], mbr.memberName].Value
|
||||
let delPrompt =
|
||||
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 [] [
|
||||
td [] [
|
||||
a [ _href (sprintf "/small-group/member/%s/edit" mbrId); _title s.["Edit This Group Member"].Value ]
|
||||
@@ -282,7 +284,7 @@ let members (mbrs : Member list) (emailTyps : Map<string, LocalizedString>) ctx
|
||||
let overview m vi =
|
||||
let s = I18N.localizer.Force ()
|
||||
let linkSpacer = rawText " "
|
||||
let typs = ReferenceList.requestTypeList s |> Map.ofList
|
||||
let typs = ReferenceList.requestTypeList s |> dict
|
||||
article [ _class "pt-overview" ] [
|
||||
section [] [
|
||||
header [ _role "heading" ] [
|
||||
@@ -348,7 +350,7 @@ let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi =
|
||||
use sw = new StringWriter ()
|
||||
let raw = rawLocText sw
|
||||
[ form [ _action "/small-group/preferences/save"; _method "post"; _class "pt-center-columns" ] [
|
||||
style [ _scoped ] [ rawText "#expireDays, #daysToKeepNew, #longTermUpdateWeeks, #headingFontSize, #listFontSize { width: 3rem; } #emailFromAddress { width: 20rem; } #listFonts { width: 40rem; } @media screen and (max-width: 40rem) { #listFonts { width: 100%; } }" ]
|
||||
style [ _scoped ] [ rawText "#expireDays, #daysToKeepNew, #longTermUpdateWeeks, #headingFontSize, #listFontSize, #pageSize { width: 3rem; } #emailFromAddress { width: 20rem; } #listFonts { width: 40rem; } @media screen and (max-width: 40rem) { #listFonts { width: 100%; } }" ]
|
||||
csrfToken ctx
|
||||
fieldset [] [
|
||||
legend [] [ strong [] [ icon "date_range"; rawText " "; locStr s.["Dates"] ] ]
|
||||
@@ -405,7 +407,9 @@ let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi =
|
||||
label [ _for "defaultEmailType" ] [ locStr s.["E-mail Format"] ]
|
||||
seq {
|
||||
yield "", selectDefault s.["Select"].Value
|
||||
yield! ReferenceList.emailTypeList "" s |> Seq.skip 1 |> Seq.map (fun typ -> fst typ, (snd typ).Value)
|
||||
yield! ReferenceList.emailTypeList HtmlFormat s
|
||||
|> Seq.skip 1
|
||||
|> Seq.map (fun typ -> fst typ, (snd typ).Value)
|
||||
}
|
||||
|> selectList "defaultEmailType" m.defaultEmailType [ _required ]
|
||||
]
|
||||
@@ -477,7 +481,7 @@ let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi =
|
||||
legend [] [ strong [] [ icon "settings"; rawText " "; locStr s.["Other Settings"] ] ]
|
||||
div [ _class "pt-field-row" ] [
|
||||
div [ _class "pt-field" ] [
|
||||
label [ _for "TimeZone" ] [ locStr s.["Time Zone"] ]
|
||||
label [ _for "timeZone" ] [ locStr s.["Time Zone"] ]
|
||||
seq {
|
||||
yield "", selectDefault s.["Select"].Value
|
||||
yield! tzs |> List.map (fun tz -> tz.timeZoneId, (TimeZones.name tz.timeZoneId s).Value)
|
||||
@@ -509,6 +513,19 @@ let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi =
|
||||
_value (match m.groupPassword with Some x -> x | None -> "") ]
|
||||
]
|
||||
]
|
||||
div [ _class "pt-field-row" ] [
|
||||
div [ _class "pt-field" ] [
|
||||
label [ _for "pageSize" ] [ locStr s.["Page Size"] ]
|
||||
input [ _type "number"; _name "pageSize"; _id "pageSize"; _min "10"; _max "255"; _required
|
||||
_value (string m.pageSize) ]
|
||||
]
|
||||
div [ _class "pt-field" ] [
|
||||
label [ _for "asOfDate" ] [ locStr s.["“As of” Date Display"] ]
|
||||
ReferenceList.asOfDateList s
|
||||
|> List.map (fun (code, desc) -> code, desc.Value)
|
||||
|> selectList "asOfDate" m.asOfDate [ _required ]
|
||||
]
|
||||
]
|
||||
div [ _class "pt-field-row" ] [ submit [] "save" s.["Save Preferences"] ]
|
||||
]
|
||||
]
|
||||
|
||||
@@ -190,7 +190,7 @@ let maintain (users : User list) ctx vi =
|
||||
|> List.map (fun user ->
|
||||
let userId = flatGuid user.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
|
||||
tr [] [
|
||||
td [] [
|
||||
|
||||
@@ -127,6 +127,20 @@ let htmlToPlainText html =
|
||||
/// Get the second portion of a tuple as a string
|
||||
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
|
||||
[<RequireQualifiedAccess>]
|
||||
module Key =
|
||||
|
||||
@@ -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 " ("; 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"
|
||||
|
||||
Reference in New Issue
Block a user