Add SQLite cache (#39)

- WIP on grid-based table display (#38)
This commit is contained in:
Daniel J. Summers 2022-08-06 16:40:51 -04:00
parent 539e20ae87
commit 57814b5bf1
5 changed files with 81 additions and 28 deletions

1
.gitignore vendored
View File

@ -332,5 +332,6 @@ ASALocalRun/
### --- ### ### --- ###
src/PrayerTracker/appsettings.json src/PrayerTracker/appsettings.json
docs/_site docs/_site
**/*.db*
.ionide .ionide

View File

@ -1,6 +1,7 @@
module PrayerTracker.Views.Church module PrayerTracker.Views.Church
open Giraffe.ViewEngine open Giraffe.ViewEngine
open Giraffe.ViewEngine.Accessibility
open PrayerTracker open PrayerTracker
open PrayerTracker.Entities open PrayerTracker.Entities
open PrayerTracker.ViewModels open PrayerTracker.ViewModels
@ -56,38 +57,72 @@ let edit (model : EditChurch) ctx viewInfo =
/// View for church maintenance page /// View for church maintenance page
let maintain (churches : Church list) (stats : Map<string, ChurchStats>) ctx vi = let maintain (churches : Church list) (stats : Map<string, ChurchStats>) ctx viewInfo =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let vi =
AppViewInfo.withScopedStyles [
"#churchList { grid-template-columns: repeat(7, auto); }"
] viewInfo
let chTbl = let chTbl =
match churches with match churches with
| [] -> space | [] -> space
| _ -> | _ ->
table [ _class "pt-table pt-action-table" ] [ section [ _id "churchList"; _class "pt-data-list"; _ariaLabel "Church list" ] [
tableHeadings s [ "Actions"; "Name"; "Location"; "Groups"; "Requests"; "Users"; "Interface?" ] header [] [ locStr s["Actions"] ]
churches header [] [ locStr s["Name"] ]
|> List.map (fun ch -> header [] [ locStr s["Location"] ]
let chId = shortGuid ch.Id.Value header [] [ locStr s["Groups"] ]
let delAction = $"/church/{chId}/delete" header [] [ locStr s["Requests"] ]
header [] [ locStr s["Users"] ]
header [] [ 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.", let delPrompt = s["Are you sure you want to delete this {0}? This action cannot be undone.",
$"""{s["Church"].Value.ToLower ()} ({ch.Name})"""] $"""{s["Church"].Value.ToLower ()} ({church.Name})"""]
tr [] [ div [ _class "row" ] [
td [] [ div [] [
a [ _href $"/church/{chId}/edit"; _title s["Edit This Church"].Value ] [ icon "edit" ] a [ _href $"/church/{churchId}/edit"; _title s["Edit This Church"].Value ] [ icon "edit" ]
a [ _href delAction a [ _href delAction
_title s["Delete This Church"].Value _title s["Delete This Church"].Value
_onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ] [ _onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ] [
icon "delete_forever" icon "delete_forever"
] ]
] ]
td [] [ str ch.Name ] div [] [ str church.Name ]
td [] [ str ch.City; rawText ", "; str ch.State ] div [] [ str church.City; rawText ", "; str church.State ]
td [ _class "pt-right-text" ] [ rawText (stats[chId].SmallGroups.ToString "N0") ] div [ _class "pt-right-text" ] [ rawText (stats[churchId].SmallGroups.ToString "N0") ]
td [ _class "pt-right-text" ] [ rawText (stats[chId].PrayerRequests.ToString "N0") ] div [ _class "pt-right-text" ] [ rawText (stats[churchId].PrayerRequests.ToString "N0") ]
td [ _class "pt-right-text" ] [ rawText (stats[chId].Users.ToString "N0") ] div [ _class "pt-right-text" ] [ rawText (stats[churchId].Users.ToString "N0") ]
td [ _class "pt-center-text" ] [ locStr s[if ch.HasVpsInterface then "Yes" else "No"] ] div [ _class "pt-center-text" ] [ locStr s[if church.HasVpsInterface then "Yes" else "No"] ]
]) ]
|> tbody []
] ]
// 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" ] [ [ div [ _class "pt-center-text" ] [
br [] br []
a [ _href $"/church/{emptyGuid}/edit"; _title s["Add a New Church"].Value ] [ a [ _href $"/church/{emptyGuid}/edit"; _title s["Add a New Church"].Value ] [

View File

@ -36,18 +36,20 @@ module Configure =
(ctx.Configuration.GetSection >> opts.Configure >> ignore) "Kestrel" (ctx.Configuration.GetSection >> opts.Configure >> ignore) "Kestrel"
open System.Globalization open System.Globalization
open System.IO
open Microsoft.AspNetCore.Authentication.Cookies open Microsoft.AspNetCore.Authentication.Cookies
open Microsoft.AspNetCore.Localization open Microsoft.AspNetCore.Localization
open Microsoft.EntityFrameworkCore open Microsoft.EntityFrameworkCore
open Microsoft.Extensions.DependencyInjection open Microsoft.Extensions.DependencyInjection
open NeoSmart.Caching.Sqlite
open NodaTime open NodaTime
/// Configure ASP.NET Core's service collection (dependency injection container) /// Configure ASP.NET Core's service collection (dependency injection container)
let services (svc : IServiceCollection) = let services (svc : IServiceCollection) =
let _ = svc.AddOptions() let _ = svc.AddOptions ()
let _ = svc.AddLocalization(fun options -> options.ResourcesPath <- "Resources") let _ = svc.AddLocalization (fun options -> options.ResourcesPath <- "Resources")
let _ = let _ =
svc.Configure<RequestLocalizationOptions>(fun (opts : RequestLocalizationOptions) -> svc.Configure<RequestLocalizationOptions> (fun (opts : RequestLocalizationOptions) ->
let supportedCultures =[| let supportedCultures =[|
CultureInfo "en-US"; CultureInfo "en-GB"; CultureInfo "en-AU"; CultureInfo "en" CultureInfo "en-US"; CultureInfo "en-GB"; CultureInfo "en-AU"; CultureInfo "en"
CultureInfo "es-MX"; CultureInfo "es-ES"; CultureInfo "es" CultureInfo "es-MX"; CultureInfo "es-ES"; CultureInfo "es"
@ -57,20 +59,20 @@ module Configure =
opts.SupportedUICultures <- supportedCultures) opts.SupportedUICultures <- supportedCultures)
let _ = let _ =
svc.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) svc.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(fun opts -> .AddCookie (fun opts ->
opts.ExpireTimeSpan <- TimeSpan.FromMinutes 120. opts.ExpireTimeSpan <- TimeSpan.FromMinutes 120.
opts.SlidingExpiration <- true opts.SlidingExpiration <- true
opts.AccessDeniedPath <- "/error/403") opts.AccessDeniedPath <- "/error/403")
let _ = svc.AddAuthorization () let _ = svc.AddAuthorization ()
let _ = svc.AddDistributedMemoryCache() let _ = svc.AddSqliteCache (fun opts -> opts.CachePath <- Path.Combine (".", "session.db"))
let _ = svc.AddSession() let _ = svc.AddSession ()
let _ = svc.AddAntiforgery() let _ = svc.AddAntiforgery ()
let _ = svc.AddRouting() let _ = svc.AddRouting ()
let _ = svc.AddSingleton<IClock> SystemClock.Instance let _ = svc.AddSingleton<IClock> SystemClock.Instance
let config = svc.BuildServiceProvider().GetRequiredService<IConfiguration> () let config = svc.BuildServiceProvider().GetRequiredService<IConfiguration> ()
let _ = let _ =
svc.AddDbContext<AppDbContext>( svc.AddDbContext<AppDbContext> (
(fun options -> options.UseNpgsql (config.GetConnectionString "PrayerTracker") |> ignore), (fun options -> options.UseNpgsql (config.GetConnectionString "PrayerTracker") |> ignore),
ServiceLifetime.Scoped, ServiceLifetime.Singleton) ServiceLifetime.Scoped, ServiceLifetime.Singleton)
() ()

View File

@ -26,6 +26,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Giraffe" Version="6.0.0" /> <PackageReference Include="Giraffe" Version="6.0.0" />
<PackageReference Include="Giraffe.Htmx" Version="1.8.0" /> <PackageReference Include="Giraffe.Htmx" Version="1.8.0" />
<PackageReference Include="NeoSmart.Caching.Sqlite" Version="6.0.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.5" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.5" />
<PackageReference Update="FSharp.Core" Version="6.0.5" /> <PackageReference Update="FSharp.Core" Version="6.0.5" />
</ItemGroup> </ItemGroup>

View File

@ -262,6 +262,20 @@ footer a:hover {
.pt-right-text { .pt-right-text {
text-align: right; text-align: right;
} }
.pt-data-list {
display: grid;
justify-content: center;
grid-row-gap: .5rem;
grid-column-gap: 1rem;
}
.pt-data-list .row {
display: contents;
}
.pt-data-list .row:hover > * {
background-color: purple;
}
.pt-table { .pt-table {
margin: auto; margin: auto;
border-collapse: collapse; border-collapse: collapse;