From 57814b5bf11116d8ae922ade88f22a2a724b5327 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Sat, 6 Aug 2022 16:40:51 -0400 Subject: [PATCH] Add SQLite cache (#39) - WIP on grid-based table display (#38) --- .gitignore | 1 + src/PrayerTracker.UI/Church.fs | 73 +++++++++++++++++++------- src/PrayerTracker/App.fs | 20 +++---- src/PrayerTracker/PrayerTracker.fsproj | 1 + src/PrayerTracker/wwwroot/css/app.css | 14 +++++ 5 files changed, 81 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index 72d666f..09c26fe 100644 --- a/.gitignore +++ b/.gitignore @@ -332,5 +332,6 @@ ASALocalRun/ ### --- ### src/PrayerTracker/appsettings.json docs/_site +**/*.db* .ionide \ No newline at end of file diff --git a/src/PrayerTracker.UI/Church.fs b/src/PrayerTracker.UI/Church.fs index 64d1104..0f8ba8e 100644 --- a/src/PrayerTracker.UI/Church.fs +++ b/src/PrayerTracker.UI/Church.fs @@ -1,6 +1,7 @@ module PrayerTracker.Views.Church open Giraffe.ViewEngine +open Giraffe.ViewEngine.Accessibility open PrayerTracker open PrayerTracker.Entities open PrayerTracker.ViewModels @@ -56,38 +57,72 @@ let edit (model : EditChurch) ctx viewInfo = /// View for church maintenance page -let maintain (churches : Church list) (stats : Map) ctx vi = +let maintain (churches : Church list) (stats : Map) ctx viewInfo = let s = I18N.localizer.Force () + let vi = + AppViewInfo.withScopedStyles [ + "#churchList { grid-template-columns: repeat(7, auto); }" + ] viewInfo let chTbl = match churches with | [] -> space | _ -> - 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" + 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?"] ] + 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 ()} ({ch.Name})"""] - tr [] [ - td [] [ - a [ _href $"/church/{chId}/edit"; _title s["Edit This Church"].Value ] [ icon "edit" ] + $"""{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" ] ] - 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 [] [ 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"] ] + ] ] + // 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 ] [ diff --git a/src/PrayerTracker/App.fs b/src/PrayerTracker/App.fs index 56456a5..127566b 100644 --- a/src/PrayerTracker/App.fs +++ b/src/PrayerTracker/App.fs @@ -36,18 +36,20 @@ module Configure = (ctx.Configuration.GetSection >> opts.Configure >> ignore) "Kestrel" open System.Globalization + open System.IO open Microsoft.AspNetCore.Authentication.Cookies open Microsoft.AspNetCore.Localization open Microsoft.EntityFrameworkCore open Microsoft.Extensions.DependencyInjection + open NeoSmart.Caching.Sqlite open NodaTime /// Configure ASP.NET Core's service collection (dependency injection container) let services (svc : IServiceCollection) = - let _ = svc.AddOptions() - let _ = svc.AddLocalization(fun options -> options.ResourcesPath <- "Resources") + let _ = svc.AddOptions () + let _ = svc.AddLocalization (fun options -> options.ResourcesPath <- "Resources") let _ = - svc.Configure(fun (opts : RequestLocalizationOptions) -> + svc.Configure (fun (opts : RequestLocalizationOptions) -> let supportedCultures =[| CultureInfo "en-US"; CultureInfo "en-GB"; CultureInfo "en-AU"; CultureInfo "en" CultureInfo "es-MX"; CultureInfo "es-ES"; CultureInfo "es" @@ -57,20 +59,20 @@ module Configure = opts.SupportedUICultures <- supportedCultures) let _ = svc.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) - .AddCookie(fun opts -> + .AddCookie (fun opts -> opts.ExpireTimeSpan <- TimeSpan.FromMinutes 120. opts.SlidingExpiration <- true opts.AccessDeniedPath <- "/error/403") let _ = svc.AddAuthorization () - let _ = svc.AddDistributedMemoryCache() - let _ = svc.AddSession() - let _ = svc.AddAntiforgery() - let _ = svc.AddRouting() + let _ = svc.AddSqliteCache (fun opts -> opts.CachePath <- Path.Combine (".", "session.db")) + let _ = svc.AddSession () + let _ = svc.AddAntiforgery () + let _ = svc.AddRouting () let _ = svc.AddSingleton SystemClock.Instance let config = svc.BuildServiceProvider().GetRequiredService () let _ = - svc.AddDbContext( + svc.AddDbContext ( (fun options -> options.UseNpgsql (config.GetConnectionString "PrayerTracker") |> ignore), ServiceLifetime.Scoped, ServiceLifetime.Singleton) () diff --git a/src/PrayerTracker/PrayerTracker.fsproj b/src/PrayerTracker/PrayerTracker.fsproj index 712cdfe..5a01ab7 100644 --- a/src/PrayerTracker/PrayerTracker.fsproj +++ b/src/PrayerTracker/PrayerTracker.fsproj @@ -26,6 +26,7 @@ + diff --git a/src/PrayerTracker/wwwroot/css/app.css b/src/PrayerTracker/wwwroot/css/app.css index 3172cdc..55e5e64 100644 --- a/src/PrayerTracker/wwwroot/css/app.css +++ b/src/PrayerTracker/wwwroot/css/app.css @@ -262,6 +262,20 @@ footer a:hover { .pt-right-text { 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 { margin: auto; border-collapse: collapse;