From f6c483b7c08b8bc84abd249d0ccdebe762baca6d Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Wed, 3 Aug 2022 14:36:00 -0400 Subject: [PATCH] First cut of blue theme (#38) - Fix onload scripts with htmx navigation (#36) - Convert db to use identifiers that do not require quoting --- src/PrayerTracker.Data/AppDbContext.fs | 16 +- src/PrayerTracker.Data/DataAccess.fs | 12 +- src/PrayerTracker.Data/Entities.fs | 391 ++++---- .../20161217153124_InitialDatabase.fs | 835 ++++++++---------- .../Migrations/AppDbContextModelSnapshot.fs | 330 ++++--- src/PrayerTracker.Tests/Data/EntitiesTests.fs | 66 +- src/PrayerTracker.Tests/UI/ViewModelsTests.fs | 17 +- src/PrayerTracker.UI/Church.fs | 2 +- src/PrayerTracker.UI/Layout.fs | 38 +- src/PrayerTracker.UI/PrayerRequest.fs | 71 +- src/PrayerTracker.UI/SmallGroup.fs | 349 ++++---- src/PrayerTracker.UI/User.fs | 4 +- src/PrayerTracker.UI/Utils.fs | 8 +- src/PrayerTracker.UI/ViewModels.fs | 25 +- src/PrayerTracker/PrayerRequest.fs | 42 +- src/PrayerTracker/SmallGroup.fs | 4 +- src/PrayerTracker/User.fs | 7 +- src/PrayerTracker/wwwroot/css/app.css | 85 +- src/names-to-lower.sql | 106 +++ 19 files changed, 1167 insertions(+), 1241 deletions(-) create mode 100644 src/names-to-lower.sql diff --git a/src/PrayerTracker.Data/AppDbContext.fs b/src/PrayerTracker.Data/AppDbContext.fs index bad2e09..e095a33 100644 --- a/src/PrayerTracker.Data/AppDbContext.fs +++ b/src/PrayerTracker.Data/AppDbContext.fs @@ -78,13 +78,13 @@ type AppDbContext (options : DbContextOptions) = modelBuilder.HasDefaultSchema "pt" |> ignore - [ Church.configureEF - ListPreferences.configureEF - Member.configureEF - PrayerRequest.configureEF - SmallGroup.configureEF - TimeZone.configureEF - User.configureEF - UserSmallGroup.configureEF + [ Church.ConfigureEF + ListPreferences.ConfigureEF + Member.ConfigureEF + PrayerRequest.ConfigureEF + SmallGroup.ConfigureEF + TimeZone.ConfigureEF + User.ConfigureEF + UserSmallGroup.ConfigureEF ] |> List.iter (fun x -> x modelBuilder) diff --git a/src/PrayerTracker.Data/DataAccess.fs b/src/PrayerTracker.Data/DataAccess.fs index 23dbd29..870701c 100644 --- a/src/PrayerTracker.Data/DataAccess.fs +++ b/src/PrayerTracker.Data/DataAccess.fs @@ -91,7 +91,7 @@ type AppDbContext with /// Get all (or active) requests for a small group as of now or the specified date member this.AllRequestsForSmallGroup (grp : SmallGroup) clock listDate activeOnly pageNbr = backgroundTask { - let theDate = match listDate with Some dt -> dt | _ -> grp.localDateNow clock + let theDate = match listDate with Some dt -> dt | _ -> grp.LocalDateNow clock let query = this.PrayerRequests.Where(fun req -> req.SmallGroupId = grp.Id) |> function @@ -120,15 +120,15 @@ type AppDbContext with return! this.PrayerRequests.CountAsync (fun pr -> pr.SmallGroup.ChurchId = churchId) } - /// Get all (or active) requests for a small group as of now or the specified date + /// Search requests for a small group using the given case-insensitive search term member this.SearchRequestsForSmallGroup (grp : SmallGroup) (searchTerm : string) pageNbr = backgroundTask { let sql = """ - SELECT * FROM pt."PrayerRequest" WHERE "SmallGroupId" = {0} AND "Text" ILIKE {1} + SELECT * FROM pt.prayer_request WHERE small_group_id = {0} AND request_text ILIKE {1} UNION - SELECT * FROM pt."PrayerRequest" WHERE "SmallGroupId" = {0} AND COALESCE("Requestor", '') ILIKE {1}""" + SELECT * FROM pt.prayer_request WHERE small_group_id = {0} AND COALESCE(requestor, '') ILIKE {1}""" let like = sprintf "%%%s%%" let query = - this.PrayerRequests.FromSqlRaw(sql, grp.Id, like searchTerm) + this.PrayerRequests.FromSqlRaw (sql, grp.Id.Value, like searchTerm) |> reqSort grp.Preferences.RequestSort |> paginate pageNbr grp.Preferences.PageSize let! reqs = query.ToListAsync () @@ -255,7 +255,7 @@ type AppDbContext with /// Get all PrayerTracker users as members (used to send e-mails) member this.AllUsersAsMembers () = backgroundTask { let! users = this.AllUsers () - return users |> List.map (fun u -> { Member.empty with Email = u.Email; Name = u.fullName }) + return users |> List.map (fun u -> { Member.empty with Email = u.Email; Name = u.Name }) } /// Find a user based on their credentials diff --git a/src/PrayerTracker.Data/Entities.fs b/src/PrayerTracker.Data/Entities.fs index a00049b..0b7b964 100644 --- a/src/PrayerTracker.Data/Entities.fs +++ b/src/PrayerTracker.Data/Entities.fs @@ -394,8 +394,8 @@ type [] Church = /// The 2-letter state or province code for the church's location State : string - /// Does this church have an active interface with Virtual Prayer Room? - HasInterface : bool + /// Does this church have an active interface with Virtual Prayer Space? + HasVpsInterface : bool /// The address for the interface InterfaceAddress : string option @@ -411,24 +411,27 @@ with Name = "" City = "" State = "" - HasInterface = false + HasVpsInterface = false InterfaceAddress = None SmallGroups = List () } /// Configure EF for this entity - static member internal configureEF (mb : ModelBuilder) = - mb.Entity (fun m -> - m.ToTable "Church" |> ignore - m.Property(fun e -> e.Id).HasColumnName "ChurchId" |> ignore - m.Property(fun e -> e.Name).HasColumnName("Name").IsRequired () |> ignore - m.Property(fun e -> e.City).HasColumnName("City").IsRequired () |> ignore - m.Property(fun e -> e.State).HasColumnName("ST").IsRequired().HasMaxLength 2 |> ignore - m.Property(fun e -> e.HasInterface).HasColumnName "HasVirtualPrayerRoomInterface" |> ignore - m.Property(fun e -> e.InterfaceAddress).HasColumnName "InterfaceAddress" |> ignore) + static member internal ConfigureEF (mb : ModelBuilder) = + mb.Entity (fun it -> + seq { + it.ToTable "church" + it.Property(fun c -> c.Id).HasColumnName "id" + it.Property(fun c -> c.Name).HasColumnName("church_name").IsRequired () + it.Property(fun c -> c.City).HasColumnName("city").IsRequired () + it.Property(fun c -> c.State).HasColumnName("state").IsRequired().HasMaxLength 2 + it.Property(fun c -> c.HasVpsInterface).HasColumnName "has_vps_interface" + it.Property(fun c -> c.InterfaceAddress).HasColumnName "interface_address" + } |> List.ofSeq |> ignore) |> ignore - mb.Model.FindEntityType(typeof).FindProperty("Id").SetValueConverter(Converters.ChurchIdConverter ()) - mb.Model.FindEntityType(typeof).FindProperty("InterfaceAddress") + mb.Model.FindEntityType(typeof).FindProperty(nameof Church.empty.Id) + .SetValueConverter(Converters.ChurchIdConverter ()) + mb.Model.FindEntityType(typeof).FindProperty(nameof Church.empty.InterfaceAddress) .SetValueConverter(OptionConverter ()) @@ -517,108 +520,50 @@ with } /// Configure EF for this entity - static member internal configureEF (mb : ModelBuilder) = - mb.Entity (fun m -> - m.ToTable "ListPreference" |> ignore - m.HasKey (fun e -> e.SmallGroupId :> obj) |> ignore - m.Property(fun e -> e.SmallGroupId).HasColumnName "SmallGroupId" |> ignore - m.Property(fun e -> e.DaysToKeepNew) - .HasColumnName("DaysToKeepNew") - .IsRequired() - .HasDefaultValue 7 - |> ignore - m.Property(fun e -> e.DaysToExpire) - .HasColumnName("DaysToExpire") - .IsRequired() - .HasDefaultValue 14 - |> ignore - m.Property(fun e -> e.LongTermUpdateWeeks) - .HasColumnName("LongTermUpdateWeeks") - .IsRequired() - .HasDefaultValue 4 - |> ignore - m.Property(fun e -> e.EmailFromName) - .HasColumnName("EmailFromName") - .IsRequired() - .HasDefaultValue "PrayerTracker" - |> ignore - m.Property(fun e -> e.EmailFromAddress) - .HasColumnName("EmailFromAddress") - .IsRequired() - .HasDefaultValue "prayer@djs-consulting.com" - |> ignore - m.Property(fun e -> e.Fonts) - .HasColumnName("ListFonts") - .IsRequired() - .HasDefaultValue "Century Gothic,Tahoma,Luxi Sans,sans-serif" - |> ignore - m.Property(fun e -> e.HeadingColor) - .HasColumnName("HeadingColor") - .IsRequired() - .HasDefaultValue "maroon" - |> ignore - m.Property(fun e -> e.LineColor) - .HasColumnName("LineColor") - .IsRequired() - .HasDefaultValue "navy" - |> ignore - m.Property(fun e -> e.HeadingFontSize) - .HasColumnName("HeadingFontSize") - .IsRequired() - .HasDefaultValue 16 - |> ignore - m.Property(fun e -> e.TextFontSize) - .HasColumnName("TextFontSize") - .IsRequired() - .HasDefaultValue 12 - |> ignore - m.Property(fun e -> e.RequestSort) - .HasColumnName("RequestSort") - .IsRequired() - .HasMaxLength(1) - .HasDefaultValue SortByDate - |> ignore - m.Property(fun e -> e.GroupPassword) - .HasColumnName("GroupPassword") - .IsRequired() - .HasDefaultValue "" - |> ignore - m.Property(fun e -> e.DefaultEmailType) - .HasColumnName("DefaultEmailType") - .IsRequired() - .HasDefaultValue HtmlFormat - |> ignore - m.Property(fun e -> e.IsPublic) - .HasColumnName("IsPublic") - .IsRequired() - .HasDefaultValue false - |> ignore - m.Property(fun e -> e.TimeZoneId) - .HasColumnName("TimeZoneId") - .IsRequired() - .HasDefaultValue "America/Denver" - |> ignore - m.Property(fun e -> e.PageSize) - .HasColumnName("PageSize") - .IsRequired() - .HasDefaultValue 100 - |> ignore - m.Property(fun e -> e.AsOfDateDisplay) - .HasColumnName("AsOfDateDisplay") - .IsRequired() - .HasMaxLength(1) - .HasDefaultValue NoDisplay - |> ignore) + static member internal ConfigureEF (mb : ModelBuilder) = + mb.Entity (fun it -> + seq { + it.ToTable "list_preference" + it.HasKey (fun lp -> lp.SmallGroupId :> obj) + it.Property(fun lp -> lp.SmallGroupId).HasColumnName "small_group_id" + it.Property(fun lp -> lp.DaysToKeepNew).HasColumnName("days_to_keep_new").IsRequired().HasDefaultValue 7 + it.Property(fun lp -> lp.DaysToExpire).HasColumnName("days_to_expire").IsRequired().HasDefaultValue 14 + it.Property(fun lp -> lp.LongTermUpdateWeeks).HasColumnName("long_term_update_weeks").IsRequired() + .HasDefaultValue 4 + it.Property(fun lp -> lp.EmailFromName).HasColumnName("email_from_name").IsRequired() + .HasDefaultValue "PrayerTracker" + it.Property(fun lp -> lp.EmailFromAddress).HasColumnName("email_from_address").IsRequired() + .HasDefaultValue "prayer@djs-consulting.com" + it.Property(fun lp -> lp.Fonts).HasColumnName("fonts").IsRequired() + .HasDefaultValue "Century Gothic,Tahoma,Luxi Sans,sans-serif" + it.Property(fun lp -> lp.HeadingColor).HasColumnName("heading_color").IsRequired() + .HasDefaultValue "maroon" + it.Property(fun lp -> lp.LineColor).HasColumnName("line_color").IsRequired().HasDefaultValue "navy" + it.Property(fun lp -> lp.HeadingFontSize).HasColumnName("heading_font_size").IsRequired() + .HasDefaultValue 16 + it.Property(fun lp -> lp.TextFontSize).HasColumnName("text_font_size").IsRequired().HasDefaultValue 12 + it.Property(fun lp -> lp.RequestSort).HasColumnName("request_sort").IsRequired().HasMaxLength(1) + .HasDefaultValue SortByDate + it.Property(fun lp -> lp.GroupPassword).HasColumnName("group_password").IsRequired().HasDefaultValue "" + it.Property(fun lp -> lp.DefaultEmailType).HasColumnName("default_email_type").IsRequired() + .HasDefaultValue HtmlFormat + it.Property(fun lp -> lp.IsPublic).HasColumnName("is_public").IsRequired().HasDefaultValue false + it.Property(fun lp -> lp.TimeZoneId).HasColumnName("time_zone_id").IsRequired() + .HasDefaultValue (TimeZoneId "America/Denver") + it.Property(fun lp -> lp.PageSize).HasColumnName("page_size").IsRequired().HasDefaultValue 100 + it.Property(fun lp -> lp.AsOfDateDisplay).HasColumnName("as_of_date_display").IsRequired() + .HasMaxLength(1).HasDefaultValue NoDisplay + } |> List.ofSeq |> ignore) |> ignore - mb.Model.FindEntityType(typeof).FindProperty("SmallGroupId") + mb.Model.FindEntityType(typeof).FindProperty(nameof ListPreferences.empty.SmallGroupId) .SetValueConverter(Converters.SmallGroupIdConverter ()) - mb.Model.FindEntityType(typeof).FindProperty("RequestSort") + mb.Model.FindEntityType(typeof).FindProperty(nameof ListPreferences.empty.RequestSort) .SetValueConverter(Converters.RequestSortConverter ()) - mb.Model.FindEntityType(typeof).FindProperty("DefaultEmailType") + mb.Model.FindEntityType(typeof).FindProperty(nameof ListPreferences.empty.DefaultEmailType) .SetValueConverter(Converters.EmailFormatConverter ()) - mb.Model.FindEntityType(typeof).FindProperty("TimeZoneId") + mb.Model.FindEntityType(typeof).FindProperty(nameof ListPreferences.empty.TimeZoneId) .SetValueConverter(Converters.TimeZoneIdConverter ()) - mb.Model.FindEntityType(typeof).FindProperty("AsOfDateDisplay") + mb.Model.FindEntityType(typeof).FindProperty(nameof ListPreferences.empty.AsOfDateDisplay) .SetValueConverter(Converters.AsOfDateDisplayConverter ()) @@ -655,19 +600,22 @@ with } /// Configure EF for this entity - static member internal configureEF (mb : ModelBuilder) = - mb.Entity (fun m -> - m.ToTable "Member" |> ignore - m.Property(fun e -> e.Id).HasColumnName "MemberId" |> ignore - m.Property(fun e -> e.SmallGroupId).HasColumnName "SmallGroupId" |> ignore - m.Property(fun e -> e.Name).HasColumnName("MemberName").IsRequired() |> ignore - m.Property(fun e -> e.Email).HasColumnName("Email").IsRequired() |> ignore - m.Property(fun e -> e.Format).HasColumnName "Format" |> ignore) + static member internal ConfigureEF (mb : ModelBuilder) = + mb.Entity (fun it -> + seq { + it.ToTable "member" + it.Property(fun m -> m.Id).HasColumnName "id" + it.Property(fun m -> m.SmallGroupId).HasColumnName("small_group_id").IsRequired () + it.Property(fun m -> m.Name).HasColumnName("member_name").IsRequired () + it.Property(fun m -> m.Email).HasColumnName("email").IsRequired () + it.Property(fun m -> m.Format).HasColumnName "email_format" + } |> List.ofSeq |> ignore) |> ignore - mb.Model.FindEntityType(typeof).FindProperty("Id").SetValueConverter(Converters.MemberIdConverter ()) - mb.Model.FindEntityType(typeof).FindProperty("SmallGroupId") + mb.Model.FindEntityType(typeof).FindProperty(nameof Member.empty.Id) + .SetValueConverter(Converters.MemberIdConverter ()) + mb.Model.FindEntityType(typeof).FindProperty(nameof Member.empty.SmallGroupId) .SetValueConverter(Converters.SmallGroupIdConverter ()) - mb.Model.FindEntityType(typeof).FindProperty("Format") + mb.Model.FindEntityType(typeof).FindProperty(nameof Member.empty.Format) .SetValueConverter(Converters.EmailFormatOptionConverter ()) @@ -713,63 +661,62 @@ with /// An empty request static member empty = - { Id = PrayerRequestId Guid.Empty - RequestType = CurrentRequest - UserId = UserId Guid.Empty - SmallGroupId = SmallGroupId Guid.Empty - EnteredDate = DateTime.MinValue - UpdatedDate = DateTime.MinValue - Requestor = None - Text = "" - NotifyChaplain = false - User = User.empty - SmallGroup = SmallGroup.empty - Expiration = Automatic + { Id = PrayerRequestId Guid.Empty + RequestType = CurrentRequest + UserId = UserId Guid.Empty + SmallGroupId = SmallGroupId Guid.Empty + EnteredDate = DateTime.MinValue + UpdatedDate = DateTime.MinValue + Requestor = None + Text = "" + NotifyChaplain = false + User = User.empty + SmallGroup = SmallGroup.empty + Expiration = Automatic } /// Is this request expired? - member this.isExpired (curr : DateTime) expDays = - match this.Expiration with - | Forced -> true - | Manual -> false - | Automatic -> - match this.RequestType with - | LongTermRequest - | Expecting -> false - | _ -> curr.AddDays(-(float expDays)).Date > this.UpdatedDate.Date // Automatic expiration + member this.IsExpired (curr : DateTime) expDays = + match this.Expiration, this.RequestType with + | Forced, _ -> true + | Manual, _ + | Automatic, LongTermRequest + | Automatic, Expecting -> false + | Automatic, _ -> curr.AddDays(-(float expDays)).Date > this.UpdatedDate.Date // Automatic expiration /// Is an update required for this long-term request? - member this.updateRequired curr expDays updWeeks = - match this.isExpired curr expDays with - | true -> false - | false -> curr.AddDays(-(float (updWeeks * 7))).Date > this.UpdatedDate.Date + member this.UpdateRequired curr expDays updWeeks = + if this.IsExpired curr expDays then false + else curr.AddDays(-(float (updWeeks * 7))).Date > this.UpdatedDate.Date /// Configure EF for this entity - static member internal configureEF (mb : ModelBuilder) = - mb.Entity (fun m -> - m.ToTable "PrayerRequest" |> ignore - m.Property(fun e -> e.Id).HasColumnName "PrayerRequestId" |> ignore - m.Property(fun e -> e.RequestType).HasColumnName("RequestType").IsRequired() |> ignore - m.Property(fun e -> e.UserId).HasColumnName "UserId" |> ignore - m.Property(fun e -> e.SmallGroupId).HasColumnName "SmallGroupId" |> ignore - m.Property(fun e -> e.EnteredDate).HasColumnName "EnteredDate" |> ignore - m.Property(fun e -> e.UpdatedDate).HasColumnName "UpdatedDate" |> ignore - m.Property(fun e -> e.Requestor).HasColumnName "Requestor" |> ignore - m.Property(fun e -> e.Text).HasColumnName("Text").IsRequired() |> ignore - m.Property(fun e -> e.NotifyChaplain).HasColumnName "NotifyChaplain" |> ignore - m.Property(fun e -> e.Expiration).HasColumnName "Expiration" |> ignore) + static member internal ConfigureEF (mb : ModelBuilder) = + mb.Entity (fun it -> + seq { + it.ToTable "prayer_request" + it.Property(fun pr -> pr.Id).HasColumnName "id" + it.Property(fun pr -> pr.RequestType).HasColumnName("request_type").IsRequired () + it.Property(fun pr -> pr.UserId).HasColumnName "user_id" + it.Property(fun pr -> pr.SmallGroupId).HasColumnName "small_group_id" + it.Property(fun pr -> pr.EnteredDate).HasColumnName "entered_date" + it.Property(fun pr -> pr.UpdatedDate).HasColumnName "updated_date" + it.Property(fun pr -> pr.Requestor).HasColumnName "requestor" + it.Property(fun pr -> pr.Text).HasColumnName("request_text").IsRequired () + it.Property(fun pr -> pr.NotifyChaplain).HasColumnName "notify_chaplain" + it.Property(fun pr -> pr.Expiration).HasColumnName "expiration" + } |> List.ofSeq |> ignore) |> ignore - mb.Model.FindEntityType(typeof).FindProperty("Id") + mb.Model.FindEntityType(typeof).FindProperty(nameof PrayerRequest.empty.Id) .SetValueConverter(Converters.PrayerRequestIdConverter ()) - mb.Model.FindEntityType(typeof).FindProperty("RequestType") + mb.Model.FindEntityType(typeof).FindProperty(nameof PrayerRequest.empty.RequestType) .SetValueConverter(Converters.PrayerRequestTypeConverter ()) - mb.Model.FindEntityType(typeof).FindProperty("UserId") + mb.Model.FindEntityType(typeof).FindProperty(nameof PrayerRequest.empty.UserId) .SetValueConverter(Converters.UserIdConverter ()) - mb.Model.FindEntityType(typeof).FindProperty("SmallGroupId") + mb.Model.FindEntityType(typeof).FindProperty(nameof PrayerRequest.empty.SmallGroupId) .SetValueConverter(Converters.SmallGroupIdConverter ()) - mb.Model.FindEntityType(typeof).FindProperty("Requestor") + mb.Model.FindEntityType(typeof).FindProperty(nameof PrayerRequest.empty.Requestor) .SetValueConverter(OptionConverter ()) - mb.Model.FindEntityType(typeof).FindProperty("Expiration") + mb.Model.FindEntityType(typeof).FindProperty(nameof PrayerRequest.empty.Expiration) .SetValueConverter(Converters.ExpirationConverter ()) @@ -814,8 +761,8 @@ with } /// Get the local date for this group - member this.localTimeNow (clock : IClock) = - match clock with null -> nullArg "clock" | _ -> () + member this.LocalTimeNow (clock : IClock) = + if isNull clock then nullArg (nameof clock) let tzId = TimeZoneId.toString this.Preferences.TimeZoneId let tz = if DateTimeZoneProviders.Tzdb.Ids.Contains tzId then DateTimeZoneProviders.Tzdb[tzId] @@ -823,21 +770,26 @@ with clock.GetCurrentInstant().InZone(tz).ToDateTimeUnspecified () /// Get the local date for this group - member this.localDateNow clock = - (this.localTimeNow clock).Date + member this.LocalDateNow clock = + (this.LocalTimeNow clock).Date /// Configure EF for this entity - static member internal configureEF (mb : ModelBuilder) = - mb.Entity (fun m -> - m.ToTable "SmallGroup" |> ignore - m.Property(fun e -> e.Id).HasColumnName "SmallGroupId" |> ignore - m.Property(fun e -> e.ChurchId).HasColumnName "ChurchId" |> ignore - m.Property(fun e -> e.Name).HasColumnName("Name").IsRequired() |> ignore - m.HasOne(fun e -> e.Preferences) |> ignore) + static member internal ConfigureEF (mb : ModelBuilder) = + mb.Entity (fun it -> + seq { + it.ToTable "small_group" + it.Property(fun sg -> sg.Id).HasColumnName "id" + it.Property(fun sg -> sg.ChurchId).HasColumnName "church_id" + it.Property(fun sg -> sg.Name).HasColumnName("group_name").IsRequired () + it.HasOne(fun sg -> sg.Preferences) + .WithOne() + .HasPrincipalKey(fun sg -> sg.Id :> obj) + .HasForeignKey(fun (lp : ListPreferences) -> lp.SmallGroupId :> obj) + } |> List.ofSeq |> ignore) |> ignore - mb.Model.FindEntityType(typeof).FindProperty("Id") + mb.Model.FindEntityType(typeof).FindProperty(nameof SmallGroup.empty.Id) .SetValueConverter(Converters.SmallGroupIdConverter ()) - mb.Model.FindEntityType(typeof).FindProperty("ChurchId") + mb.Model.FindEntityType(typeof).FindProperty(nameof SmallGroup.empty.ChurchId) .SetValueConverter(Converters.ChurchIdConverter ()) @@ -866,15 +818,17 @@ with } /// Configure EF for this entity - static member internal configureEF (mb : ModelBuilder) = - mb.Entity ( fun m -> - m.ToTable "TimeZone" |> ignore - m.Property(fun e -> e.Id).HasColumnName "TimeZoneId" |> ignore - m.Property(fun e -> e.Description).HasColumnName("Description").IsRequired() |> ignore - m.Property(fun e -> e.SortOrder).HasColumnName "SortOrder" |> ignore - m.Property(fun e -> e.IsActive).HasColumnName "IsActive" |> ignore) + static member internal ConfigureEF (mb : ModelBuilder) = + mb.Entity (fun it -> + seq { + it.ToTable "time_zone" + it.Property(fun tz -> tz.Id).HasColumnName "id" + it.Property(fun tz -> tz.Description).HasColumnName("description").IsRequired () + it.Property(fun tz -> tz.SortOrder).HasColumnName "sort_order" + it.Property(fun tz -> tz.IsActive).HasColumnName "is_active" + } |> List.ofSeq |> ignore) |> ignore - mb.Model.FindEntityType(typeof).FindProperty("Id") + mb.Model.FindEntityType(typeof).FindProperty(nameof TimeZone.empty.Id) .SetValueConverter(Converters.TimeZoneIdConverter ()) @@ -919,24 +873,27 @@ with } /// The full name of the user - member this.fullName = + member this.Name = $"{this.FirstName} {this.LastName}" /// Configure EF for this entity - static member internal configureEF (mb : ModelBuilder) = - mb.Entity (fun m -> - m.ToTable "User" |> ignore - m.Ignore(fun e -> e.fullName :> obj) |> ignore - m.Property(fun e -> e.Id).HasColumnName "UserId" |> ignore - m.Property(fun e -> e.FirstName).HasColumnName("FirstName").IsRequired() |> ignore - m.Property(fun e -> e.LastName).HasColumnName("LastName").IsRequired() |> ignore - m.Property(fun e -> e.Email).HasColumnName("EmailAddress").IsRequired() |> ignore - m.Property(fun e -> e.IsAdmin).HasColumnName "IsSystemAdmin" |> ignore - m.Property(fun e -> e.PasswordHash).HasColumnName("PasswordHash").IsRequired() |> ignore - m.Property(fun e -> e.Salt).HasColumnName "Salt" |> ignore) + static member internal ConfigureEF (mb : ModelBuilder) = + mb.Entity (fun it -> + seq { + it.ToTable "pt_user" + it.Ignore(fun u -> u.Name :> obj) + it.Property(fun u -> u.Id).HasColumnName "id" + it.Property(fun u -> u.FirstName).HasColumnName("first_name").IsRequired () + it.Property(fun u -> u.LastName).HasColumnName("last_name").IsRequired () + it.Property(fun u -> u.Email).HasColumnName("email").IsRequired () + it.Property(fun u -> u.IsAdmin).HasColumnName "is_admin" + it.Property(fun u -> u.PasswordHash).HasColumnName("password_hash").IsRequired () + it.Property(fun u -> u.Salt).HasColumnName "salt" + } |> List.ofSeq |> ignore) |> ignore - mb.Model.FindEntityType(typeof).FindProperty("Id").SetValueConverter(Converters.UserIdConverter ()) - mb.Model.FindEntityType(typeof).FindProperty("Salt") + mb.Model.FindEntityType(typeof).FindProperty(nameof User.empty.Id) + .SetValueConverter(Converters.UserIdConverter ()) + mb.Model.FindEntityType(typeof).FindProperty(nameof User.empty.Salt) .SetValueConverter(OptionConverter ()) @@ -965,23 +922,23 @@ with } /// Configure EF for this entity - static member internal configureEF (mb : ModelBuilder) = - mb.Entity (fun m -> - m.ToTable "User_SmallGroup" |> ignore - m.HasKey(fun e -> {| UserId = e.UserId; SmallGroupId = e.SmallGroupId |} :> obj) |> ignore - m.Property(fun e -> e.UserId).HasColumnName "UserId" |> ignore - m.Property(fun e -> e.SmallGroupId).HasColumnName "SmallGroupId" |> ignore - m.HasOne(fun e -> e.User) - .WithMany(fun e -> e.SmallGroups :> IEnumerable) - .HasForeignKey(fun e -> e.UserId :> obj) - |> ignore - m.HasOne(fun e -> e.SmallGroup) - .WithMany(fun e -> e.Users :> IEnumerable) - .HasForeignKey(fun e -> e.SmallGroupId :> obj) - |> ignore) + static member internal ConfigureEF (mb : ModelBuilder) = + mb.Entity (fun it -> + seq { + it.ToTable "user_small_group" + it.HasKey (nameof UserSmallGroup.empty.UserId, nameof UserSmallGroup.empty.SmallGroupId) + it.Property(fun usg -> usg.UserId).HasColumnName "user_id" + it.Property(fun usg -> usg.SmallGroupId).HasColumnName "small_group_id" + it.HasOne(fun usg -> usg.User) + .WithMany(fun u -> u.SmallGroups :> IEnumerable) + .HasForeignKey(fun usg -> usg.UserId :> obj) + it.HasOne(fun usg -> usg.SmallGroup) + .WithMany(fun sg -> sg.Users :> IEnumerable) + .HasForeignKey(fun usg -> usg.SmallGroupId :> obj) + } |> List.ofSeq |> ignore) |> ignore - mb.Model.FindEntityType(typeof).FindProperty("UserId") + mb.Model.FindEntityType(typeof).FindProperty(nameof UserSmallGroup.empty.UserId) .SetValueConverter(Converters.UserIdConverter ()) - mb.Model.FindEntityType(typeof).FindProperty("SmallGroupId") + mb.Model.FindEntityType(typeof).FindProperty(nameof UserSmallGroup.empty.SmallGroupId) .SetValueConverter(Converters.SmallGroupIdConverter ()) \ No newline at end of file diff --git a/src/PrayerTracker.Data/Migrations/20161217153124_InitialDatabase.fs b/src/PrayerTracker.Data/Migrations/20161217153124_InitialDatabase.fs index c228771..bc7a5f3 100644 --- a/src/PrayerTracker.Data/Migrations/20161217153124_InitialDatabase.fs +++ b/src/PrayerTracker.Data/Migrations/20161217153124_InitialDatabase.fs @@ -1,514 +1,389 @@ namespace PrayerTracker.Migrations +open System open Microsoft.EntityFrameworkCore open Microsoft.EntityFrameworkCore.Infrastructure open Microsoft.EntityFrameworkCore.Migrations -open Microsoft.EntityFrameworkCore.Migrations.Operations -open Microsoft.EntityFrameworkCore.Migrations.Operations.Builders open Npgsql.EntityFrameworkCore.PostgreSQL.Metadata open PrayerTracker open PrayerTracker.Entities -open System - -// fsharplint:disable RecordFieldNames - -type ChurchTable = - { churchId : OperationBuilder - city : OperationBuilder - hasInterface : OperationBuilder - interfaceAddress : OperationBuilder - name : OperationBuilder - st : OperationBuilder - } - -type ListPreferencesTable = - { smallGroupId : OperationBuilder - daysToExpire : OperationBuilder - daysToKeepNew : OperationBuilder - defaultEmailType : OperationBuilder - emailFromAddress : OperationBuilder - emailFromName : OperationBuilder - groupPassword : OperationBuilder - headingColor : OperationBuilder - headingFontSize : OperationBuilder - isPublic : OperationBuilder - lineColor : OperationBuilder - listFonts : OperationBuilder - longTermUpdateWeeks : OperationBuilder - requestSort : OperationBuilder - textFontSize : OperationBuilder - timeZoneId : OperationBuilder - pageSize : OperationBuilder - asOfDateDisplay : OperationBuilder - } - -type MemberTable = - { memberId : OperationBuilder - email : OperationBuilder - format : OperationBuilder - memberName : OperationBuilder - smallGroupId : OperationBuilder - } - -type PrayerRequestTable = - { prayerRequestId : OperationBuilder - enteredDate : OperationBuilder - expiration : OperationBuilder - notifyChaplain : OperationBuilder - requestType : OperationBuilder - requestor : OperationBuilder - smallGroupId : OperationBuilder - text : OperationBuilder - updatedDate : OperationBuilder - userId : OperationBuilder - } - -type SmallGroupTable = - { smallGroupId : OperationBuilder - churchId : OperationBuilder - name : OperationBuilder - } - -type TimeZoneTable = - { timeZoneId : OperationBuilder - description : OperationBuilder - isActive : OperationBuilder - sortOrder : OperationBuilder - } - -type UserSmallGroupTable = - { userId : OperationBuilder - smallGroupId : OperationBuilder - } - -type UserTable = - { userId : OperationBuilder - emailAddress : OperationBuilder - firstName : OperationBuilder - isAdmin : OperationBuilder - lastName : OperationBuilder - passwordHash : OperationBuilder - salt : OperationBuilder - } [)>] [] type InitialDatabase () = - inherit Migration () - override __.Up (migrationBuilder : MigrationBuilder) = - migrationBuilder.EnsureSchema (name = "pt") - |> ignore + inherit Migration () + override _.Up (migrationBuilder : MigrationBuilder) = + migrationBuilder.EnsureSchema (name = "pt") + |> ignore - migrationBuilder.CreateTable ( - name = "Church", - schema = "pt", - columns = - (fun table -> - { churchId = table.Column (name = "ChurchId", nullable = false) - city = table.Column (name = "City", nullable = false) - hasInterface = table.Column (name = "HasVirtualPrayerRoomInterface", nullable = false) - interfaceAddress = table.Column (name = "InterfaceAddress", nullable = true) - name = table.Column (name = "Name", nullable = false) - st = table.Column (name = "ST", nullable = false, maxLength = Nullable 2) - }), - constraints = - fun table -> - table.PrimaryKey ("PK_Church", fun x -> upcast x.churchId) |> ignore) - |> ignore + migrationBuilder.CreateTable ( + name = "church", + schema = "pt", + columns = (fun table -> + {| Id = table.Column (name = "id", nullable = false) + City = table.Column (name = "city", nullable = false) + HasVpsInterface = table.Column (name = "has_vps_interface", nullable = false) + InterfaceAddress = table.Column (name = "interface_address", nullable = true) + Name = table.Column (name = "church_Name", nullable = false) + State = table.Column (name = "state", nullable = false, maxLength = Nullable 2) + |}), + constraints = fun table -> + table.PrimaryKey ("pk_church", fun x -> upcast x.Id) |> ignore) + |> ignore - migrationBuilder.CreateTable ( - name = "TimeZone", - schema = "pt", - columns = - (fun table -> - { timeZoneId = table.Column (name = "TimeZoneId", nullable = false) - description = table.Column (name = "Description", nullable = false) - isActive = table.Column (name = "IsActive", nullable = false) - sortOrder = table.Column (name = "SortOrder", nullable = false) - }), - constraints = - fun table -> - table.PrimaryKey ("PK_TimeZone", fun x -> upcast x.timeZoneId) |> ignore) - |> ignore + migrationBuilder.CreateTable ( + name = "time_zone", + schema = "pt", + columns = (fun table -> + {| Id = table.Column (name = "id", nullable = false) + Description = table.Column (name = "description", nullable = false) + IsActive = table.Column (name = "is_active", nullable = false) + SortOrder = table.Column (name = "sort_order", nullable = false) + |}), + constraints = fun table -> + table.PrimaryKey ("pk_time_zone", fun x -> upcast x.Id) |> ignore) + |> ignore - migrationBuilder.CreateTable ( - name = "User", - schema = "pt", - columns = - (fun table -> - { userId = table.Column (name = "UserId", nullable = false) - emailAddress = table.Column (name = "EmailAddress", nullable = false) - firstName = table.Column (name = "FirstName", nullable = false) - isAdmin = table.Column (name = "IsSystemAdmin", nullable = false) - lastName = table.Column (name = "LastName", nullable = false) - passwordHash = table.Column (name = "PasswordHash", nullable = false) - salt = table.Column (name = "Salt", nullable = true) - }), - constraints = - fun table -> - table.PrimaryKey("PK_User", fun x -> upcast x.userId) |> ignore) - |> ignore + migrationBuilder.CreateTable ( + name = "pt_user", + schema = "pt", + columns = (fun table -> + {| Id = table.Column (name = "id", nullable = false) + Email = table.Column (name = "email", nullable = false) + FirstName = table.Column (name = "first_name", nullable = false) + IsAdmin = table.Column (name = "is_admin", nullable = false) + LastName = table.Column (name = "last_name", nullable = false) + PasswordHash = table.Column (name = "password_hash", nullable = false) + Salt = table.Column (name = "salt", nullable = true) + |}), + constraints = fun table -> + table.PrimaryKey("pk_pt_user", fun x -> upcast x.Id) |> ignore) + |> ignore - migrationBuilder.CreateTable ( - name = "SmallGroup", - schema = "pt", - columns = - (fun table -> - { smallGroupId = table.Column (name = "SmallGroupId", nullable = false) - churchId = table.Column (name = "ChurchId", nullable = false) - name = table.Column (name = "Name", nullable = false) - }), - constraints = - fun table -> - table.PrimaryKey ("PK_SmallGroup", fun x -> upcast x.smallGroupId) |> ignore - table.ForeignKey ( - name = "FK_SmallGroup_Church_ChurchId", - column = (fun x -> upcast x.churchId), - principalSchema = "pt", - principalTable = "Church", - principalColumn = "ChurchId", - onDelete = ReferentialAction.Cascade) - |> ignore) - |> ignore + migrationBuilder.CreateTable ( + name = "small_group", + schema = "pt", + columns = (fun table -> + {| Id = table.Column (name = "id", nullable = false) + ChurchId = table.Column (name = "church_id", nullable = false) + Name = table.Column (name = "group_name", nullable = false) + |}), + constraints = fun table -> + table.PrimaryKey ("pk_small_group", fun x -> upcast x.Id) |> ignore + table.ForeignKey ( + name = "fk_small_group_church_id", + column = (fun x -> upcast x.ChurchId), + principalSchema = "pt", + principalTable = "church", + principalColumn = "id", + onDelete = ReferentialAction.Cascade) + |> ignore) + |> ignore - migrationBuilder.CreateTable ( - name = "ListPreference", - schema = "pt", - columns = - (fun table -> - { smallGroupId = table.Column (name = "SmallGroupId", nullable = false) - daysToExpire = table.Column (name = "DaysToExpire", nullable = false, defaultValue = 14) - daysToKeepNew = table.Column (name = "DaysToKeepNew", nullable = false, defaultValue = 7) - defaultEmailType = table.Column (name = "DefaultEmailType", nullable = false, defaultValue = "Html") - emailFromAddress = table.Column (name = "EmailFromAddress", nullable = false, defaultValue = "prayer@djs-consulting.com") - emailFromName = table.Column (name = "EmailFromName", nullable = false, defaultValue = "PrayerTracker") - groupPassword = table.Column (name = "GroupPassword", nullable = false, defaultValue = "") - headingColor = table.Column (name = "HeadingColor", nullable = false, defaultValue = "maroon") - headingFontSize = table.Column (name = "HeadingFontSize", nullable = false, defaultValue = 16) - isPublic = table.Column (name = "IsPublic", nullable = false, defaultValue = false) - lineColor = table.Column (name = "LineColor", nullable = false, defaultValue = "navy") - listFonts = table.Column (name = "ListFonts", nullable = false, defaultValue = "Century Gothic,Tahoma,Luxi Sans,sans-serif") - longTermUpdateWeeks = table.Column (name = "LongTermUpdateWeeks", nullable = false, defaultValue = 4) - requestSort = table.Column (name = "RequestSort", nullable = false, defaultValue = "D", maxLength = Nullable 1) - textFontSize = table.Column (name = "TextFontSize", nullable = false, defaultValue = 12) - timeZoneId = table.Column (name = "TimeZoneId", nullable = false, defaultValue = "America/Denver") - pageSize = table.Column (name = "PageSize", nullable = false, defaultValue = 100) - asOfDateDisplay = table.Column (name = "AsOfDateDisplay", nullable = false, defaultValue = "N", maxLength = Nullable 1) - }), - constraints = - fun table -> - table.PrimaryKey ("PK_ListPreference", fun x -> upcast x.smallGroupId) |> ignore - table.ForeignKey ( - name = "FK_ListPreference_SmallGroup_SmallGroupId", - column = (fun x -> upcast x.smallGroupId), - principalSchema = "pt", - principalTable = "SmallGroup", - principalColumn = "SmallGroupId", - onDelete = ReferentialAction.Cascade) - |> ignore - table.ForeignKey ( - name = "FK_ListPreference_TimeZone_TimeZoneId", - column = (fun x -> upcast x.timeZoneId), - principalSchema = "pt", - principalTable = "TimeZone", - principalColumn = "TimeZoneId", - onDelete = ReferentialAction.Cascade) - |> ignore) - |> ignore - - migrationBuilder.CreateTable ( - name = "Member", - schema = "pt", - columns = - (fun table -> - { memberId = table.Column (name = "MemberId", nullable = false) - email = table.Column (name = "Email", nullable = false) - format = table.Column (name = "Format", nullable = true) - memberName = table.Column (name = "MemberName", nullable = false) - smallGroupId = table.Column (name = "SmallGroupId", nullable = false) - }), - constraints = - fun table -> - table.PrimaryKey ("PK_Member", fun x -> upcast x.memberId) |> ignore - table.ForeignKey ( - name = "FK_Member_SmallGroup_SmallGroupId", - column = (fun x -> upcast x.smallGroupId), - principalSchema = "pt", - principalTable = "SmallGroup", - principalColumn = "SmallGroupId", - onDelete = ReferentialAction.Cascade) - |> ignore) - |> ignore + migrationBuilder.CreateTable ( + name = "list_preference", + schema = "pt", + columns = (fun table -> + {| SmallGroupId = table.Column (name = "small_group_id", nullable = false) + AsOfDateDisplay = table.Column (name = "as_of_date_display", nullable = false, defaultValue = "N", maxLength = Nullable 1) + DaysToExpire = table.Column (name = "days_to_expire", nullable = false, defaultValue = 14) + DaysToKeepNew = table.Column (name = "days_to_keep_new", nullable = false, defaultValue = 7) + DefaultEmailType = table.Column (name = "default_email_type", nullable = false, defaultValue = "Html") + EmailFromAddress = table.Column (name = "email_from_address", nullable = false, defaultValue = "prayer@djs-consulting.com") + EmailFromName = table.Column (name = "email_from_name", nullable = false, defaultValue = "PrayerTracker") + Fonts = table.Column (name = "fonts", nullable = false, defaultValue = "Century Gothic,Tahoma,Luxi Sans,sans-serif") + GroupPassword = table.Column (name = "group_password", nullable = false, defaultValue = "") + HeadingColor = table.Column (name = "heading_color", nullable = false, defaultValue = "maroon") + HeadingFontSize = table.Column (name = "heading_font_size", nullable = false, defaultValue = 16) + IsPublic = table.Column (name = "is_public", nullable = false, defaultValue = false) + LineColor = table.Column (name = "line_color", nullable = false, defaultValue = "navy") + LongTermUpdateWeeks = table.Column (name = "long_term_update_weeks", nullable = false, defaultValue = 4) + PageSize = table.Column (name = "page_size", nullable = false, defaultValue = 100) + RequestSort = table.Column (name = "request_sort", nullable = false, defaultValue = "D", maxLength = Nullable 1) + TextFontSize = table.Column (name = "text_font_size", nullable = false, defaultValue = 12) + TimeZoneId = table.Column (name = "time_zone_id", nullable = false, defaultValue = "America/Denver") + |}), + constraints = fun table -> + table.PrimaryKey ("pk_list_preference", fun x -> upcast x.SmallGroupId) |> ignore + table.ForeignKey ( + name = "fk_list_preference_small_group_id", + column = (fun x -> upcast x.SmallGroupId), + principalSchema = "pt", + principalTable = "small_group", + principalColumn = "id", + onDelete = ReferentialAction.Cascade) + |> ignore + table.ForeignKey ( + name = "fk_list_preference_time_zone_id", + column = (fun x -> upcast x.TimeZoneId), + principalSchema = "pt", + principalTable = "time_zone", + principalColumn = "id", + onDelete = ReferentialAction.Cascade) + |> ignore) + |> ignore + + migrationBuilder.CreateTable ( + name = "member", + schema = "pt", + columns = (fun table -> + {| Id = table.Column (name = "id", nullable = false) + Email = table.Column (name = "email", nullable = false) + Format = table.Column (name = "email_format", nullable = true) + Name = table.Column (name = "member_name", nullable = false) + SmallGroupId = table.Column (name = "small_group_id", nullable = false) + |}), + constraints = fun table -> + table.PrimaryKey ("pk_member", fun x -> upcast x.Id) |> ignore + table.ForeignKey ( + name = "fk_member_small_group_id", + column = (fun x -> upcast x.SmallGroupId), + principalSchema = "pt", + principalTable = "small_group", + principalColumn = "id", + onDelete = ReferentialAction.Cascade) + |> ignore) + |> ignore - migrationBuilder.CreateTable ( - name = "PrayerRequest", - schema = "pt", - columns = - (fun table -> - { prayerRequestId = table.Column (name = "PrayerRequestId", nullable = false) - expiration = table.Column (name = "Expiration", nullable = false) - enteredDate = table.Column (name = "EnteredDate", nullable = false) - notifyChaplain = table.Column (name = "NotifyChaplain", nullable = false) - requestType = table.Column (name = "RequestType", nullable = false) - requestor = table.Column (name = "Requestor", nullable = true) - smallGroupId = table.Column (name = "SmallGroupId", nullable = false) - text = table.Column (name = "Text", nullable = false) - updatedDate = table.Column (name = "UpdatedDate", nullable = false) - userId = table.Column (name = "UserId", nullable = false) - }), - constraints = - fun table -> - table.PrimaryKey ("PK_PrayerRequest", fun x -> upcast x.prayerRequestId) |> ignore - table.ForeignKey ( - name = "FK_PrayerRequest_SmallGroup_SmallGroupId", - column = (fun x -> upcast x.smallGroupId), - principalSchema = "pt", - principalTable = "SmallGroup", - principalColumn = "SmallGroupId", - onDelete = ReferentialAction.Cascade) - |> ignore - table.ForeignKey ( - name = "FK_PrayerRequest_User_UserId", - column = (fun x -> upcast x.userId), - principalSchema = "pt", - principalTable = "User", - principalColumn = "UserId", - onDelete = ReferentialAction.Cascade) - |> ignore) - |> ignore - - migrationBuilder.CreateTable( - name = "User_SmallGroup", - schema = "pt", - columns = - (fun table -> - { userId = table.Column (name = "UserId", nullable = false) - smallGroupId = table.Column (name = "SmallGroupId", nullable = false) - }), - constraints = - fun table -> - table.PrimaryKey ("PK_User_SmallGroup", fun x -> upcast x) |> ignore - table.ForeignKey ( - name = "FK_User_SmallGroup_SmallGroup_SmallGroupId", - column = (fun x -> upcast x.smallGroupId), - principalSchema = "pt", - principalTable = "SmallGroup", - principalColumn = "SmallGroupId", - onDelete = ReferentialAction.Cascade) - |> ignore - table.ForeignKey ( - name = "FK_User_SmallGroup_User_UserId", - column = (fun x -> upcast x.userId), - principalSchema = "pt", - principalTable = "User", - principalColumn = "UserId", - onDelete = ReferentialAction.Cascade) - |> ignore) - |> ignore + migrationBuilder.CreateTable ( + name = "prayer_request", + schema = "pt", + columns = (fun table -> + {| Id = table.Column (name = "id", nullable = false) + Expiration = table.Column (name = "expiration", nullable = false) + EnteredDate = table.Column (name = "entered_date", nullable = false) + NotifyChaplain = table.Column (name = "notify_chaplain", nullable = false) + RequestType = table.Column (name = "request_type", nullable = false) + Requestor = table.Column (name = "requestor", nullable = true) + SmallGroupId = table.Column (name = "small_group_id", nullable = false) + Text = table.Column (name = "request_text", nullable = false) + UpdatedDate = table.Column (name = "updated_date", nullable = false) + UserId = table.Column (name = "user_id", nullable = false) + |}), + constraints = fun table -> + table.PrimaryKey ("pk_prayer_request", fun x -> upcast x.Id) |> ignore + table.ForeignKey ( + name = "fk_prayer_request_small_group_id", + column = (fun x -> upcast x.SmallGroupId), + principalSchema = "pt", + principalTable = "small_group", + principalColumn = "i", + onDelete = ReferentialAction.Cascade) + |> ignore + table.ForeignKey ( + name = "fk_prayer_request_user_id", + column = (fun x -> upcast x.UserId), + principalSchema = "pt", + principalTable = "pt_user", + principalColumn = "id", + onDelete = ReferentialAction.Cascade) + |> ignore) + |> ignore + + migrationBuilder.CreateTable( + name = "user_small_group", + schema = "pt", + columns = (fun table -> + {| UserId = table.Column (name = "user_id", nullable = false) + SmallGroupId = table.Column (name = "small_group_id", nullable = false) + |}), + constraints = fun table -> + table.PrimaryKey ("pk_user_small_group", fun x -> upcast x) |> ignore + table.ForeignKey ( + name = "fk_user_small_group_small_group_id", + column = (fun x -> upcast x.SmallGroupId), + principalSchema = "pt", + principalTable = "small_group", + principalColumn = "id", + onDelete = ReferentialAction.Cascade) + |> ignore + table.ForeignKey ( + name = "fk_user_small_group_user_id", + column = (fun x -> upcast x.UserId), + principalSchema = "pt", + principalTable = "pt_user", + principalColumn = "id", + onDelete = ReferentialAction.Cascade) + |> ignore) + |> ignore - migrationBuilder.CreateIndex (name = "IX_ListPreference_TimeZoneId", schema = "pt", table = "ListPreference", column = "TimeZoneId") |> ignore - migrationBuilder.CreateIndex (name = "IX_Member_SmallGroupId", schema = "pt", table = "Member", column = "SmallGroupId") |> ignore - migrationBuilder.CreateIndex (name = "IX_PrayerRequest_SmallGroupId", schema = "pt", table = "PrayerRequest", column = "SmallGroupId") |> ignore - migrationBuilder.CreateIndex (name = "IX_PrayerRequest_UserId", schema = "pt", table = "PrayerRequest", column = "UserId") |> ignore - migrationBuilder.CreateIndex (name = "IX_SmallGroup_ChurchId", schema = "pt", table = "SmallGroup", column = "ChurchId") |> ignore - migrationBuilder.CreateIndex (name = "IX_User_SmallGroup_SmallGroupId", schema = "pt", table = "User_SmallGroup", column = "SmallGroupId") |> ignore + migrationBuilder.CreateIndex (name = "ix_list_preference_time_zone_id", schema = "pt", table = "list_preference", column = "time_zone_id") |> ignore + migrationBuilder.CreateIndex (name = "ix_member_small_group_id", schema = "pt", table = "member", column = "small_group_id") |> ignore + migrationBuilder.CreateIndex (name = "ix_prayer_request_small_group_id", schema = "pt", table = "prayer_request", column = "small_group_id") |> ignore + migrationBuilder.CreateIndex (name = "ix_prayer_request_user_id", schema = "pt", table = "prayer_request", column = "user_id") |> ignore + migrationBuilder.CreateIndex (name = "ix_small_group_church_id", schema = "pt", table = "small_group", column = "church_id") |> ignore + migrationBuilder.CreateIndex (name = "ix_user_small_group_small_group_id", schema = "pt", table = "user_small_group", column = "small_group_id") |> ignore - override __.Down (migrationBuilder : MigrationBuilder) = - migrationBuilder.DropTable (name = "ListPreference", schema = "pt") |> ignore - migrationBuilder.DropTable (name = "Member", schema = "pt") |> ignore - migrationBuilder.DropTable (name = "PrayerRequest", schema = "pt") |> ignore - migrationBuilder.DropTable (name = "User_SmallGroup", schema = "pt") |> ignore - migrationBuilder.DropTable (name = "TimeZone", schema = "pt") |> ignore - migrationBuilder.DropTable (name = "SmallGroup", schema = "pt") |> ignore - migrationBuilder.DropTable (name = "User", schema = "pt") |> ignore - migrationBuilder.DropTable (name = "Church", schema = "pt") |> ignore + override _.Down (migrationBuilder : MigrationBuilder) = + migrationBuilder.DropTable (name = "list_preference", schema = "pt") |> ignore + migrationBuilder.DropTable (name = "member", schema = "pt") |> ignore + migrationBuilder.DropTable (name = "prayer_request", schema = "pt") |> ignore + migrationBuilder.DropTable (name = "user_small_group", schema = "pt") |> ignore + migrationBuilder.DropTable (name = "time_zone", schema = "pt") |> ignore + migrationBuilder.DropTable (name = "small_group", schema = "pt") |> ignore + migrationBuilder.DropTable (name = "pt_user", schema = "pt") |> ignore + migrationBuilder.DropTable (name = "church", schema = "pt") |> ignore - override __.BuildTargetModel (modelBuilder : ModelBuilder) = - modelBuilder - .HasDefaultSchema("pt") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) - .HasAnnotation("ProductVersion", "1.1.0-rtm-22752") - |> ignore + override _.BuildTargetModel (modelBuilder : ModelBuilder) = + modelBuilder + .HasDefaultSchema("pt") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) + .HasAnnotation("ProductVersion", "1.1.0-rtm-22752") + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.Property("churchId").ValueGeneratedOnAdd() |> ignore - b.Property("city").IsRequired() |> ignore - b.Property("hasInterface") |> ignore - b.Property("interfaceAddress") |> ignore - b.Property("name").IsRequired() |> ignore - b.Property("st").IsRequired().HasMaxLength(2) |> ignore - b.HasKey("churchId") |> ignore - b.ToTable("Church") |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.Property("Id").HasColumnName("id").ValueGeneratedOnAdd() |> ignore + b.Property("City").HasColumnName("city").IsRequired() |> ignore + b.Property("HasVpsInterface").HasColumnName("has_vps_interface") |> ignore + b.Property("InterfaceAddress").HasColumnName("interface_address") |> ignore + b.Property("Name").HasColumnName("church_name").IsRequired() |> ignore + b.Property("State").HasColumnName("state").IsRequired().HasMaxLength(2) |> ignore + b.HasKey("Id") |> ignore + b.ToTable("church") |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.Property("smallGroupId") |> ignore - b.Property("daysToExpire").ValueGeneratedOnAdd().HasDefaultValue(14) |> ignore - b.Property("daysToKeepNew").ValueGeneratedOnAdd().HasDefaultValue(7) |> ignore - b.Property("defaultEmailType").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("H") |> ignore - b.Property("emailFromAddress").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("prayer@djs-consulting.com") |> ignore - b.Property("emailFromName").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("PrayerTracker") |> ignore - b.Property("groupPassword").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("") |> ignore - b.Property("headingColor").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("maroon") |> ignore - b.Property("headingFontSize").ValueGeneratedOnAdd().HasDefaultValue(16) |> ignore - b.Property("isPublic").ValueGeneratedOnAdd().HasDefaultValue(false) |> ignore - b.Property("lineColor").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("navy") |> ignore - b.Property("listFonts").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("Century Gothic,Tahoma,Luxi Sans,sans-serif") |> ignore - b.Property("longTermUpdateWeeks").ValueGeneratedOnAdd().HasDefaultValue(4) |> ignore - b.Property("requestSort").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("D").HasMaxLength(1) |> ignore - b.Property("textFontSize").ValueGeneratedOnAdd().HasDefaultValue(12) |> ignore - b.Property("timeZoneId").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("America/Denver") |> ignore - b.Property("pageSize").IsRequired().ValueGeneratedOnAdd().HasDefaultValue(100) |> ignore - b.Property("asOfDateDisplay").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("N").HasMaxLength(1) |> ignore - b.HasKey("smallGroupId") |> ignore - b.HasIndex("timeZoneId") |> ignore - b.ToTable("ListPreference") |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.Property("SmallGroupId").HasColumnName("small_group_id") |> ignore + b.Property("AsOfDateDisplay").HasColumnName("as_of_date_display").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("N").HasMaxLength(1) |> ignore + b.Property("DaysToExpire").HasColumnName("days_to_expire").ValueGeneratedOnAdd().HasDefaultValue(14) |> ignore + b.Property("DaysToKeepNew").HasColumnName("days_to_keep_new").ValueGeneratedOnAdd().HasDefaultValue(7) |> ignore + b.Property("DefaultEmailType").HasColumnName("default_email_type").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("H") |> ignore + b.Property("EmailFromAddress").HasColumnName("email_from_address").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("prayer@djs-consulting.com") |> ignore + b.Property("EmailFromName").HasColumnName("email_from_name").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("PrayerTracker") |> ignore + b.Property("Fonts").HasColumnName("fonts").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("Century Gothic,Tahoma,Luxi Sans,sans-serif") |> ignore + b.Property("GroupPassword").HasColumnName("group_password").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("") |> ignore + b.Property("HeadingColor").HasColumnName("heading_color").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("maroon") |> ignore + b.Property("HeadingFontSize").HasColumnName("heading_font_size").ValueGeneratedOnAdd().HasDefaultValue(16) |> ignore + b.Property("IsPublic").HasColumnName("is_public").ValueGeneratedOnAdd().HasDefaultValue(false) |> ignore + b.Property("LineColor").HasColumnName("line_color").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("navy") |> ignore + b.Property("LongTermUpdateWeeks").HasColumnName("long_term_update_weeks").ValueGeneratedOnAdd().HasDefaultValue(4) |> ignore + b.Property("PageSize").HasColumnName("page_size").IsRequired().ValueGeneratedOnAdd().HasDefaultValue(100) |> ignore + b.Property("RequestSort").HasColumnName("request_sort").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("D").HasMaxLength(1) |> ignore + b.Property("TextFontSize").HasColumnName("text_font_size").ValueGeneratedOnAdd().HasDefaultValue(12) |> ignore + b.Property("TimeZoneId").HasColumnName("time_zone_id").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("America/Denver") |> ignore + b.HasKey("SmallGroupId") |> ignore + b.HasIndex("TimeZoneId").HasDatabaseName "ix_list_preference_time_zone_id" |> ignore + b.ToTable("list_preference") |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.Property("memberId").ValueGeneratedOnAdd() |> ignore - b.Property("email").IsRequired() |> ignore - b.Property("format") |> ignore - b.Property("memberName").IsRequired() |> ignore - b.Property("smallGroupId") |> ignore - b.HasKey("memberId") |> ignore - b.HasIndex("smallGroupId") |> ignore - b.ToTable("Member") |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.Property("Id").HasColumnName("id").ValueGeneratedOnAdd() |> ignore + b.Property("Email").HasColumnName("email").IsRequired() |> ignore + b.Property("Format").HasColumnName("email_format") |> ignore + b.Property("Name").HasColumnName("member_name").IsRequired() |> ignore + b.Property("SmallGroupId").HasColumnName("small_group_id") |> ignore + b.HasKey("Id") |> ignore + b.HasIndex("SmallGroupId").HasDatabaseName "ix_member_small_group_id" |> ignore + b.ToTable("member") |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.Property("prayerRequestId").ValueGeneratedOnAdd() |> ignore - b.Property("enteredDate").IsRequired() |> ignore - b.Property("expiration").IsRequired().HasMaxLength 1 |> ignore - b.Property("notifyChaplain") |> ignore - b.Property("requestType").IsRequired().HasMaxLength 1 |> ignore - b.Property("requestor") |> ignore - b.Property("smallGroupId") |> ignore - b.Property("text").IsRequired() |> ignore - b.Property("updatedDate") |> ignore - b.Property("userId") |> ignore - b.HasKey("prayerRequestId") |> ignore - b.HasIndex("smallGroupId") |> ignore - b.HasIndex("userId") |> ignore - b.ToTable("PrayerRequest") |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.Property("Id").HasColumnName("id").ValueGeneratedOnAdd() |> ignore + b.Property("EnteredDate").HasColumnName("entered_date").IsRequired() |> ignore + b.Property("Expiration").HasColumnName("expiration").IsRequired().HasMaxLength 1 |> ignore + b.Property("NotifyChaplain").HasColumnName("notify_chaplain") |> ignore + b.Property("RequestType").HasColumnName("request_type").IsRequired().HasMaxLength 1 |> ignore + b.Property("Requestor").HasColumnName("requestor") |> ignore + b.Property("SmallGroupId").HasColumnName("small_group_id") |> ignore + b.Property("Text").HasColumnName("request_text").IsRequired() |> ignore + b.Property("UpdatedDate").HasColumnName("updated_date") |> ignore + b.Property("UserId").HasColumnName("user_id") |> ignore + b.HasKey("Id") |> ignore + b.HasIndex("SmallGroupId").HasDatabaseName "ix_prayer_request_small_group_id" |> ignore + b.HasIndex("UserId").HasDatabaseName "ix_prayer_request_user_id" |> ignore + b.ToTable("prayer_request") |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.Property("smallGroupId").ValueGeneratedOnAdd() |> ignore - b.Property("churchId") |> ignore - b.Property("name").IsRequired() |> ignore - b.HasKey("smallGroupId") |> ignore - b.HasIndex("churchId") |> ignore - b.ToTable("SmallGroup") |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.Property("Id").HasColumnName("id").ValueGeneratedOnAdd() |> ignore + b.Property("ChurchId").HasColumnName("church_id") |> ignore + b.Property("Name").HasColumnName("group_name").IsRequired() |> ignore + b.HasKey("Id") |> ignore + b.HasIndex("ChurchId").HasDatabaseName "ix_small_group_church_id" |> ignore + b.ToTable("small_group") |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.Property("timeZoneId").ValueGeneratedOnAdd() |> ignore - b.Property("description").IsRequired() |> ignore - b.Property("isActive") |> ignore - b.Property("sortOrder") |> ignore - b.HasKey("timeZoneId") |> ignore - b.ToTable("TimeZone") |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.Property("Id").HasColumnName("id").ValueGeneratedOnAdd() |> ignore + b.Property("Description").HasColumnName("description").IsRequired() |> ignore + b.Property("IsActive").HasColumnName("is_active") |> ignore + b.Property("SortOrder").HasColumnName("sort_order") |> ignore + b.HasKey("Id") |> ignore + b.ToTable("time_zone") |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.Property("userId").ValueGeneratedOnAdd() |> ignore - b.Property("emailAddress").IsRequired() |> ignore - b.Property("firstName").IsRequired() |> ignore - b.Property("isAdmin") |> ignore - b.Property("lastName").IsRequired() |> ignore - b.Property("passwordHash").IsRequired() |> ignore - b.Property("salt") |> ignore - b.HasKey("userId") |> ignore - b.ToTable("User") |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.Property("Id").HasColumnName("id").ValueGeneratedOnAdd() |> ignore + b.Property("Email").HasColumnName("email").IsRequired() |> ignore + b.Property("FirstName").HasColumnName("first_name").IsRequired() |> ignore + b.Property("IsAdmin").HasColumnName("is_admin") |> ignore + b.Property("LastName").HasColumnName("last_name").IsRequired() |> ignore + b.Property("PasswordHash").HasColumnName("password_hash").IsRequired() |> ignore + b.Property("Salt").HasColumnName("salt") |> ignore + b.HasKey("Id") |> ignore + b.ToTable("pt_user") |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.Property("userId") |> ignore - b.Property("smallGroupId") |> ignore - b.HasKey("userId", "smallGroupId") |> ignore - b.HasIndex("smallGroupId") |> ignore - b.ToTable("User_SmallGroup") |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.Property("UserId").HasColumnName("user_id") |> ignore + b.Property("SmallGroupId").HasColumnName("small_group_id") |> ignore + b.HasKey("UserId", "SmallGroupId") |> ignore + b.HasIndex("SmallGroupId").HasDatabaseName "ix_user_small_group_small_group_id" |> ignore + b.ToTable("user_small_group") |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.HasOne("PrayerTracker.Entities.SmallGroup") - .WithOne("preferences") - .HasForeignKey("PrayerTracker.Entities.ListPreferences", "smallGroupId") - .OnDelete(DeleteBehavior.Cascade) - |> ignore - b.HasOne("PrayerTracker.Entities.TimeZone", "timeZone") - .WithMany() - .HasForeignKey("timeZoneId") - .OnDelete(DeleteBehavior.Cascade) - |> ignore) - |> ignore - - modelBuilder.Entity ( - typeof, - fun b -> - b.HasOne("PrayerTracker.Entities.SmallGroup", "smallGroup") - .WithMany("members") - .HasForeignKey("smallGroupId") - .OnDelete(DeleteBehavior.Cascade) - |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.HasOne("PrayerTracker.Entities.SmallGroup") + .WithOne("Preferences") + .HasForeignKey("PrayerTracker.Entities.ListPreferences", "SmallGroupId") + .OnDelete(DeleteBehavior.Cascade) + |> ignore + b.HasOne("PrayerTracker.Entities.TimeZone", "TimeZone") + .WithMany() + .HasForeignKey("TimeZoneId") + .OnDelete(DeleteBehavior.Cascade) + |> ignore) + |> ignore + + modelBuilder.Entity (typeof, fun b -> + b.HasOne("PrayerTracker.Entities.SmallGroup", "SmallGroup") + .WithMany("Members") + .HasForeignKey("SmallGroupId") + .OnDelete(DeleteBehavior.Cascade) + |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.HasOne("PrayerTracker.Entities.SmallGroup", "smallGroup") - .WithMany("prayerRequests") - .HasForeignKey("smallGroupId") - .OnDelete(DeleteBehavior.Cascade) - |> ignore - b.HasOne("PrayerTracker.Entities.User", "user") - .WithMany() - .HasForeignKey("userId") - .OnDelete(DeleteBehavior.Cascade) - |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.HasOne("PrayerTracker.Entities.SmallGroup", "SmallGroup") + .WithMany("PrayerRequests") + .HasForeignKey("SmallGroupId") + .OnDelete(DeleteBehavior.Cascade) + |> ignore + b.HasOne("PrayerTracker.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.HasOne("PrayerTracker.Entities.Church", "Church") - .WithMany("SmallGroups") - .HasForeignKey("ChurchId") - .OnDelete(DeleteBehavior.Cascade) - |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.HasOne("PrayerTracker.Entities.Church", "Church") + .WithMany("SmallGroups") + .HasForeignKey("ChurchId") + .OnDelete(DeleteBehavior.Cascade) + |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.HasOne("PrayerTracker.Entities.SmallGroup", "smallGroup") - .WithMany("users") - .HasForeignKey("smallGroupId") - .OnDelete(DeleteBehavior.Cascade) - |> ignore - b.HasOne("PrayerTracker.Entities.User", "user") - .WithMany("smallGroups") - .HasForeignKey("userId") - .OnDelete(DeleteBehavior.Cascade) - |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.HasOne("PrayerTracker.Entities.SmallGroup", "SmallGroup") + .WithMany("Users") + .HasForeignKey("SmallGroupId") + .OnDelete(DeleteBehavior.Cascade) + |> ignore + b.HasOne("PrayerTracker.Entities.User", "User") + .WithMany("SmallGroups") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + |> ignore) + |> ignore diff --git a/src/PrayerTracker.Data/Migrations/AppDbContextModelSnapshot.fs b/src/PrayerTracker.Data/Migrations/AppDbContextModelSnapshot.fs index 5ae5d5f..2b1aa10 100644 --- a/src/PrayerTracker.Data/Migrations/AppDbContextModelSnapshot.fs +++ b/src/PrayerTracker.Data/Migrations/AppDbContextModelSnapshot.fs @@ -1,200 +1,174 @@ namespace PrayerTracker.Migrations +open System open Microsoft.EntityFrameworkCore open Microsoft.EntityFrameworkCore.Infrastructure open Npgsql.EntityFrameworkCore.PostgreSQL.Metadata open PrayerTracker open PrayerTracker.Entities -open System [)>] type AppDbContextModelSnapshot () = - inherit ModelSnapshot () + inherit ModelSnapshot () - override __.BuildModel (modelBuilder : ModelBuilder) = - modelBuilder - .HasDefaultSchema("pt") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) - .HasAnnotation("ProductVersion", "1.1.0-rtm-22752") - |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.Property("churchId").ValueGeneratedOnAdd() |> ignore - b.Property("city").IsRequired() |> ignore - b.Property("hasInterface") |> ignore - b.Property("interfaceAddress") |> ignore - b.Property("name").IsRequired() |> ignore - b.Property("st").IsRequired().HasMaxLength(2) |> ignore - b.HasKey("churchId") |> ignore - b.ToTable("Church") |> ignore) - |> ignore + override _.BuildModel (modelBuilder : ModelBuilder) = + modelBuilder + .HasDefaultSchema("pt") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) + .HasAnnotation("ProductVersion", "1.1.0-rtm-22752") + |> ignore + modelBuilder.Entity (typeof, fun b -> + b.Property("Id").HasColumnName("id").ValueGeneratedOnAdd() |> ignore + b.Property("City").HasColumnName("city").IsRequired() |> ignore + b.Property("HasVpsInterface").HasColumnName("has_vps_interface") |> ignore + b.Property("InterfaceAddress").HasColumnName("interface_address") |> ignore + b.Property("Name").HasColumnName("church_name").IsRequired() |> ignore + b.Property("State").HasColumnName("state").IsRequired().HasMaxLength(2) |> ignore + b.HasKey("Id") |> ignore + b.ToTable("church") |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.Property("smallGroupId") |> ignore - b.Property("daysToExpire").ValueGeneratedOnAdd().HasDefaultValue(14) |> ignore - b.Property("daysToKeepNew").ValueGeneratedOnAdd().HasDefaultValue(7) |> ignore - b.Property("defaultEmailType").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("H").HasMaxLength(1) |> ignore - b.Property("emailFromAddress").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("prayer@djs-consulting.com") |> ignore - b.Property("emailFromName").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("PrayerTracker") |> ignore - b.Property("groupPassword").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("") |> ignore - b.Property("headingColor").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("maroon") |> ignore - b.Property("headingFontSize").ValueGeneratedOnAdd().HasDefaultValue(16) |> ignore - b.Property("isPublic").ValueGeneratedOnAdd().HasDefaultValue(false) |> ignore - b.Property("lineColor").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("navy") |> ignore - b.Property("listFonts").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("Century Gothic,Tahoma,Luxi Sans,sans-serif") |> ignore - b.Property("longTermUpdateWeeks").ValueGeneratedOnAdd().HasDefaultValue(4) |> ignore - b.Property("requestSort").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("D").HasMaxLength(1) |> ignore - b.Property("textFontSize").ValueGeneratedOnAdd().HasDefaultValue(12) |> ignore - b.Property("timeZoneId").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("America/Denver") |> ignore - b.Property("pageSize").IsRequired().ValueGeneratedOnAdd().HasDefaultValue(100) |> ignore - b.Property("asOfDateDisplay").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("N").HasMaxLength(1) |> ignore - b.HasKey("smallGroupId") |> ignore - b.HasIndex("timeZoneId") |> ignore - b.ToTable("ListPreference") |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.Property("SmallGroupId").HasColumnName("small_group_id") |> ignore + b.Property("AsOfDateDisplay").HasColumnName("as_of_date_display").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("N").HasMaxLength(1) |> ignore + b.Property("DaysToExpire").HasColumnName("days_to_expire").ValueGeneratedOnAdd().HasDefaultValue(14) |> ignore + b.Property("DaysToKeepNew").HasColumnName("days_to_keep_new").ValueGeneratedOnAdd().HasDefaultValue(7) |> ignore + b.Property("DefaultEmailType").HasColumnName("default_email_type").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("H") |> ignore + b.Property("EmailFromAddress").HasColumnName("email_from_address").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("prayer@djs-consulting.com") |> ignore + b.Property("EmailFromName").HasColumnName("email_from_name").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("PrayerTracker") |> ignore + b.Property("Fonts").HasColumnName("fonts").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("Century Gothic,Tahoma,Luxi Sans,sans-serif") |> ignore + b.Property("GroupPassword").HasColumnName("group_password").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("") |> ignore + b.Property("HeadingColor").HasColumnName("heading_color").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("maroon") |> ignore + b.Property("HeadingFontSize").HasColumnName("heading_font_size").ValueGeneratedOnAdd().HasDefaultValue(16) |> ignore + b.Property("IsPublic").HasColumnName("is_public").ValueGeneratedOnAdd().HasDefaultValue(false) |> ignore + b.Property("LineColor").HasColumnName("line_color").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("navy") |> ignore + b.Property("LongTermUpdateWeeks").HasColumnName("long_term_update_weeks").ValueGeneratedOnAdd().HasDefaultValue(4) |> ignore + b.Property("PageSize").HasColumnName("page_size").IsRequired().ValueGeneratedOnAdd().HasDefaultValue(100) |> ignore + b.Property("RequestSort").HasColumnName("request_sort").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("D").HasMaxLength(1) |> ignore + b.Property("TextFontSize").HasColumnName("text_font_size").ValueGeneratedOnAdd().HasDefaultValue(12) |> ignore + b.Property("TimeZoneId").HasColumnName("time_zone_id").IsRequired().ValueGeneratedOnAdd().HasDefaultValue("America/Denver") |> ignore + b.HasKey("SmallGroupId") |> ignore + b.HasIndex("TimeZoneId").HasDatabaseName "ix_list_preference_time_zone_id" |> ignore + b.ToTable("list_preference") |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.Property("memberId").ValueGeneratedOnAdd() |> ignore - b.Property("email").IsRequired() |> ignore - b.Property("format") |> ignore - b.Property("memberName").IsRequired() |> ignore - b.Property("smallGroupId") |> ignore - b.HasKey("memberId") |> ignore - b.HasIndex("smallGroupId") |> ignore - b.ToTable("Member") |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.Property("Id").HasColumnName("id").ValueGeneratedOnAdd() |> ignore + b.Property("Email").HasColumnName("email").IsRequired() |> ignore + b.Property("Format").HasColumnName("email_format") |> ignore + b.Property("Name").HasColumnName("member_name").IsRequired() |> ignore + b.Property("SmallGroupId").HasColumnName("small_group_id") |> ignore + b.HasKey("Id") |> ignore + b.HasIndex("SmallGroupId").HasDatabaseName "ix_member_small_group_id" |> ignore + b.ToTable("member") |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.Property("prayerRequestId").ValueGeneratedOnAdd() |> ignore - b.Property("enteredDate") |> ignore - b.Property("expiration").IsRequired().HasMaxLength(1) |> ignore - b.Property("notifyChaplain") |> ignore - b.Property("requestType").IsRequired().HasMaxLength(1) |> ignore - b.Property("requestor") |> ignore - b.Property("smallGroupId") |> ignore - b.Property("text").IsRequired() |> ignore - b.Property("updatedDate") |> ignore - b.Property("userId") |> ignore - b.HasKey("prayerRequestId") |> ignore - b.HasIndex("smallGroupId") |> ignore - b.HasIndex("userId") |> ignore - b.ToTable("PrayerRequest") |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.Property("Id").HasColumnName("id").ValueGeneratedOnAdd() |> ignore + b.Property("EnteredDate").HasColumnName("entered_date").IsRequired() |> ignore + b.Property("Expiration").HasColumnName("expiration").IsRequired().HasMaxLength 1 |> ignore + b.Property("NotifyChaplain").HasColumnName("notify_chaplain") |> ignore + b.Property("RequestType").HasColumnName("request_type").IsRequired().HasMaxLength 1 |> ignore + b.Property("Requestor").HasColumnName("requestor") |> ignore + b.Property("SmallGroupId").HasColumnName("small_group_id") |> ignore + b.Property("Text").HasColumnName("request_text").IsRequired() |> ignore + b.Property("UpdatedDate").HasColumnName("updated_date") |> ignore + b.Property("UserId").HasColumnName("user_id") |> ignore + b.HasKey("Id") |> ignore + b.HasIndex("SmallGroupId").HasDatabaseName "ix_prayer_request_small_group_id" |> ignore + b.HasIndex("UserId").HasDatabaseName "ix_prayer_request_user_id" |> ignore + b.ToTable("prayer_request") |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.Property("smallGroupId").ValueGeneratedOnAdd() |> ignore - b.Property("churchId") |> ignore - b.Property("name").IsRequired() |> ignore - b.HasKey("smallGroupId") |> ignore - b.HasIndex("churchId") |> ignore - b.ToTable("SmallGroup") |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.Property("Id").HasColumnName("id").ValueGeneratedOnAdd() |> ignore + b.Property("ChurchId").HasColumnName("church_id") |> ignore + b.Property("Name").HasColumnName("group_name").IsRequired() |> ignore + b.HasKey("Id") |> ignore + b.HasIndex("ChurchId").HasDatabaseName "ix_small_group_church_id" |> ignore + b.ToTable("small_group") |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.Property("timeZoneId").ValueGeneratedOnAdd() |> ignore - b.Property("description").IsRequired() |> ignore - b.Property("isActive") |> ignore - b.Property("sortOrder") |> ignore - b.HasKey("timeZoneId") |> ignore - b.ToTable("TimeZone") |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.Property("Id").HasColumnName("id").ValueGeneratedOnAdd() |> ignore + b.Property("Description").HasColumnName("description").IsRequired() |> ignore + b.Property("IsActive").HasColumnName("is_active") |> ignore + b.Property("SortOrder").HasColumnName("sort_order") |> ignore + b.HasKey("Id") |> ignore + b.ToTable("time_zone") |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.Property("userId").ValueGeneratedOnAdd() |> ignore - b.Property("emailAddress").IsRequired() |> ignore - b.Property("firstName").IsRequired() |> ignore - b.Property("isAdmin") |> ignore - b.Property("lastName").IsRequired() |> ignore - b.Property("passwordHash").IsRequired() |> ignore - b.Property("salt") |> ignore - b.HasKey("userId") |> ignore - b.ToTable("User") |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.Property("Id").HasColumnName("id").ValueGeneratedOnAdd() |> ignore + b.Property("Email").HasColumnName("email").IsRequired() |> ignore + b.Property("FirstName").HasColumnName("first_name").IsRequired() |> ignore + b.Property("IsAdmin").HasColumnName("is_admin") |> ignore + b.Property("LastName").HasColumnName("last_name").IsRequired() |> ignore + b.Property("PasswordHash").HasColumnName("password_hash").IsRequired() |> ignore + b.Property("Salt").HasColumnName("salt") |> ignore + b.HasKey("Id") |> ignore + b.ToTable("pt_user") |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.Property("userId") |> ignore - b.Property("smallGroupId") |> ignore - b.HasKey("userId", "smallGroupId") |> ignore - b.HasIndex("smallGroupId") |> ignore - b.ToTable("User_SmallGroup") |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.Property("UserId").HasColumnName("user_id") |> ignore + b.Property("SmallGroupId").HasColumnName("small_group_id") |> ignore + b.HasKey("UserId", "SmallGroupId") |> ignore + b.HasIndex("SmallGroupId").HasDatabaseName "ix_user_small_group_small_group_id" |> ignore + b.ToTable("user_small_group") |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.HasOne("PrayerTracker.Entities.SmallGroup") - .WithOne("preferences") - .HasForeignKey("PrayerTracker.Entities.ListPreferences", "smallGroupId") - .OnDelete(DeleteBehavior.Cascade) - |> ignore - b.HasOne("PrayerTracker.Entities.TimeZone", "timeZone") - .WithMany() - .HasForeignKey("timeZoneId") - .OnDelete(DeleteBehavior.Cascade) - |> ignore) - |> ignore - - modelBuilder.Entity ( - typeof, - fun b -> - b.HasOne("PrayerTracker.Entities.SmallGroup", "smallGroup") - .WithMany("members") - .HasForeignKey("smallGroupId") - .OnDelete(DeleteBehavior.Cascade) - |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.HasOne("PrayerTracker.Entities.SmallGroup") + .WithOne("Preferences") + .HasForeignKey("PrayerTracker.Entities.ListPreferences", "smallGroupId") + .OnDelete(DeleteBehavior.Cascade) + |> ignore + b.HasOne("PrayerTracker.Entities.TimeZone", "TimeZone") + .WithMany() + .HasForeignKey("TimeZoneId") + .OnDelete(DeleteBehavior.Cascade) + |> ignore) + |> ignore + + modelBuilder.Entity (typeof, fun b -> + b.HasOne("PrayerTracker.Entities.SmallGroup", "SmallGroup") + .WithMany("Members") + .HasForeignKey("SmallGroupId") + .OnDelete(DeleteBehavior.Cascade) + |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.HasOne("PrayerTracker.Entities.SmallGroup", "smallGroup") - .WithMany("prayerRequests") - .HasForeignKey("smallGroupId") - .OnDelete(DeleteBehavior.Cascade) - |> ignore - b.HasOne("PrayerTracker.Entities.User", "user") - .WithMany() - .HasForeignKey("userId") - .OnDelete(DeleteBehavior.Cascade) - |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.HasOne("PrayerTracker.Entities.SmallGroup", "SmallGroup") + .WithMany("PrayerRequests") + .HasForeignKey("SmallGroupId") + .OnDelete(DeleteBehavior.Cascade) + |> ignore + b.HasOne("PrayerTracker.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.HasOne("PrayerTracker.Entities.Church", "Church") - .WithMany("SmallGroups") - .HasForeignKey("ChurchId") - .OnDelete(DeleteBehavior.Cascade) - |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.HasOne("PrayerTracker.Entities.Church", "Church") + .WithMany("SmallGroups") + .HasForeignKey("ChurchId") + .OnDelete(DeleteBehavior.Cascade) + |> ignore) + |> ignore - modelBuilder.Entity ( - typeof, - fun b -> - b.HasOne("PrayerTracker.Entities.SmallGroup", "smallGroup") - .WithMany("users") - .HasForeignKey("smallGroupId") - .OnDelete(DeleteBehavior.Cascade) - |> ignore - b.HasOne("PrayerTracker.Entities.User", "user") - .WithMany("smallGroups") - .HasForeignKey("userId") - .OnDelete(DeleteBehavior.Cascade) - |> ignore) - |> ignore + modelBuilder.Entity (typeof, fun b -> + b.HasOne("PrayerTracker.Entities.SmallGroup", "SmallGroup") + .WithMany("Users") + .HasForeignKey("SmallGroupId") + .OnDelete(DeleteBehavior.Cascade) + |> ignore + b.HasOne("PrayerTracker.Entities.User", "User") + .WithMany("SmallGroups") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + |> ignore) + |> ignore diff --git a/src/PrayerTracker.Tests/Data/EntitiesTests.fs b/src/PrayerTracker.Tests/Data/EntitiesTests.fs index 0df2439..a13bd6d 100644 --- a/src/PrayerTracker.Tests/Data/EntitiesTests.fs +++ b/src/PrayerTracker.Tests/Data/EntitiesTests.fs @@ -41,7 +41,7 @@ let churchTests = Expect.equal mt.Name "" "The name should have been blank" Expect.equal mt.City "" "The city should have been blank" Expect.equal mt.State "" "The state should have been blank" - Expect.isFalse mt.HasInterface "The church should not show that it has an interface" + Expect.isFalse mt.HasVpsInterface "The church should not show that it has an interface" Expect.isNone mt.InterfaceAddress "The interface address should not exist" Expect.isNotNull mt.SmallGroups "The small groups navigation property should not be null" Expect.isEmpty mt.SmallGroups "There should be no small groups for an empty church" @@ -159,61 +159,61 @@ let prayerRequestTests = Expect.equal mt.User.Id.Value Guid.Empty "The user should have been an empty one" Expect.equal mt.SmallGroup.Id.Value Guid.Empty "The small group should have been an empty one" } - test "isExpired always returns false for expecting requests" { + test "IsExpired always returns false for expecting requests" { let req = { PrayerRequest.empty with RequestType = Expecting } - Expect.isFalse (req.isExpired DateTime.Now 0) "An expecting request should never be considered expired" + Expect.isFalse (req.IsExpired DateTime.Now 0) "An expecting request should never be considered expired" } - test "isExpired always returns false for manually-expired requests" { + test "IsExpired always returns false for manually-expired requests" { let req = { PrayerRequest.empty with UpdatedDate = DateTime.Now.AddMonths -1; Expiration = Manual } - Expect.isFalse (req.isExpired DateTime.Now 4) "A never-expired request should never be considered expired" + Expect.isFalse (req.IsExpired DateTime.Now 4) "A never-expired request should never be considered expired" } - test "isExpired always returns false for long term/recurring requests" { + test "IsExpired always returns false for long term/recurring requests" { let req = { PrayerRequest.empty with RequestType = LongTermRequest } - Expect.isFalse (req.isExpired DateTime.Now 0) + Expect.isFalse (req.IsExpired DateTime.Now 0) "A recurring/long-term request should never be considered expired" } - test "isExpired always returns true for force-expired requests" { + test "IsExpired always returns true for force-expired requests" { let req = { PrayerRequest.empty with UpdatedDate = DateTime.Now; Expiration = Forced } - Expect.isTrue (req.isExpired DateTime.Now 5) "A force-expired request should always be considered expired" + Expect.isTrue (req.IsExpired DateTime.Now 5) "A force-expired request should always be considered expired" } - test "isExpired returns false for non-expired requests" { + test "IsExpired returns false for non-expired requests" { let now = DateTime.Now let req = { PrayerRequest.empty with UpdatedDate = now.AddDays -5. } - Expect.isFalse (req.isExpired now 7) "A request updated 5 days ago should not be considered expired" + Expect.isFalse (req.IsExpired now 7) "A request updated 5 days ago should not be considered expired" } - test "isExpired returns true for expired requests" { + test "IsExpired returns true for expired requests" { let now = DateTime.Now let req = { PrayerRequest.empty with UpdatedDate = now.AddDays -8. } - Expect.isTrue (req.isExpired now 7) "A request updated 8 days ago should be considered expired" + Expect.isTrue (req.IsExpired now 7) "A request updated 8 days ago should be considered expired" } - test "isExpired returns true for same-day expired requests" { + test "IsExpired returns true for same-day expired requests" { let now = DateTime.Now let req = { PrayerRequest.empty with UpdatedDate = now.Date.AddDays(-7.).AddSeconds -1. } - Expect.isTrue (req.isExpired now 7) + Expect.isTrue (req.IsExpired now 7) "A request entered a second before midnight should be considered expired" } - test "updateRequired returns false for expired requests" { + test "UpdateRequired returns false for expired requests" { let req = { PrayerRequest.empty with Expiration = Forced } - Expect.isFalse (req.updateRequired DateTime.Now 7 4) "An expired request should not require an update" + Expect.isFalse (req.UpdateRequired DateTime.Now 7 4) "An expired request should not require an update" } - test "updateRequired returns false when an update is not required for an active request" { + test "UpdateRequired returns false when an update is not required for an active request" { let now = DateTime.Now let req = { PrayerRequest.empty with RequestType = LongTermRequest UpdatedDate = now.AddDays -14. } - Expect.isFalse (req.updateRequired now 7 4) + Expect.isFalse (req.UpdateRequired now 7 4) "An active request updated 14 days ago should not require an update until 28 days" } - test "updateRequired returns true when an update is required for an active request" { + test "UpdateRequired returns true when an update is required for an active request" { let now = DateTime.Now let req = { PrayerRequest.empty with RequestType = LongTermRequest UpdatedDate = now.AddDays -34. } - Expect.isTrue (req.updateRequired now 7 4) + Expect.isTrue (req.UpdateRequired now 7 4) "An active request updated 34 days ago should require an update (past 28 days)" } ] @@ -305,33 +305,33 @@ let smallGroupTests = Expect.isEmpty mt.Users "There should be no users for an empty small group" } yield! testFixture withFakeClock [ - "localTimeNow adjusts the time ahead of UTC", + "LocalTimeNow adjusts the time ahead of UTC", fun clock -> let grp = { SmallGroup.empty with Preferences = { ListPreferences.empty with TimeZoneId = TimeZoneId "Europe/Berlin" } } - Expect.isGreaterThan (grp.localTimeNow clock) now "UTC to Europe/Berlin should have added hours" - "localTimeNow adjusts the time behind UTC", + Expect.isGreaterThan (grp.LocalTimeNow clock) now "UTC to Europe/Berlin should have added hours" + "LocalTimeNow adjusts the time behind UTC", fun clock -> - Expect.isLessThan (SmallGroup.empty.localTimeNow clock) now + Expect.isLessThan (SmallGroup.empty.LocalTimeNow clock) now "UTC to America/Denver should have subtracted hours" - "localTimeNow returns UTC when the time zone is invalid", + "LocalTimeNow returns UTC when the time zone is invalid", fun clock -> let grp = { SmallGroup.empty with Preferences = { ListPreferences.empty with TimeZoneId = TimeZoneId "garbage" } } - Expect.equal (grp.localTimeNow clock) now "UTC should have been returned for an invalid time zone" + Expect.equal (grp.LocalTimeNow clock) now "UTC should have been returned for an invalid time zone" ] - yield test "localTimeNow fails when clock is not passed" { - Expect.throws (fun () -> (SmallGroup.empty.localTimeNow >> ignore) null) + yield test "LocalTimeNow fails when clock is not passed" { + Expect.throws (fun () -> (SmallGroup.empty.LocalTimeNow >> ignore) null) "Should have raised an exception for null clock" } - yield test "localDateNow returns the date portion" { + yield test "LocalDateNow returns the date portion" { let now' = DateTime (2017, 5, 12, 1, 15, 0, DateTimeKind.Utc) let clock = FakeClock (Instant.FromDateTimeUtc now') - Expect.isLessThan (SmallGroup.empty.localDateNow clock) now.Date "The date should have been a day earlier" + Expect.isLessThan (SmallGroup.empty.LocalDateNow clock) now.Date "The date should have been a day earlier" } ] @@ -362,9 +362,9 @@ let userTests = Expect.isNotNull mt.SmallGroups "The small groups navigation property should not have been null" Expect.isEmpty mt.SmallGroups "There should be no small groups for an empty user" } - test "fullName concatenates first and last names" { + test "Name concatenates first and last names" { let user = { User.empty with FirstName = "Unit"; LastName = "Test" } - Expect.equal user.fullName "Unit Test" "The full name should be the first and last, separated by a space" + Expect.equal user.Name "Unit Test" "The full name should be the first and last, separated by a space" } ] diff --git a/src/PrayerTracker.Tests/UI/ViewModelsTests.fs b/src/PrayerTracker.Tests/UI/ViewModelsTests.fs index 2b9d94b..629c80f 100644 --- a/src/PrayerTracker.Tests/UI/ViewModelsTests.fs +++ b/src/PrayerTracker.Tests/UI/ViewModelsTests.fs @@ -118,7 +118,6 @@ let appViewInfoTests = test "fresh is constructed properly" { let vi = AppViewInfo.fresh Expect.isEmpty vi.Style "There should have been no styles set" - Expect.isEmpty vi.Script "There should have been no scripts set" Expect.isNone vi.HelpLink "The help link should have been set to none" Expect.isEmpty vi.Messages "There should have been no messages set" Expect.equal vi.Version "" "The version should have been blank" @@ -135,7 +134,7 @@ let assignGroupsTests = let usr = { User.empty with Id = (Guid.NewGuid >> UserId) (); FirstName = "Alice"; LastName = "Bob" } let asg = AssignGroups.fromUser usr Expect.equal asg.UserId (shortGuid usr.Id.Value) "The user ID was not filled correctly" - Expect.equal asg.UserName usr.fullName "The user name was not filled correctly" + Expect.equal asg.UserName usr.Name "The user's name was not filled correctly" Expect.equal asg.SmallGroups "" "The small group string was not filled correctly" } ] @@ -150,7 +149,7 @@ let editChurchTests = Name = "Unit Test" City = "Testlandia" State = "UT" - HasInterface = true + HasVpsInterface = true InterfaceAddress = Some "https://test-dem-units.test" } let edit = EditChurch.fromChurch church @@ -159,7 +158,7 @@ let editChurchTests = Expect.equal edit.City church.City "The church's city was not filled correctly" Expect.equal edit.State church.State "The church's state was not filled correctly" Expect.isSome edit.HasInterface "The church should show that it has an interface" - Expect.equal edit.HasInterface (Some true) "The hasInterface flag should be true" + Expect.equal edit.HasInterface (Some true) "The HasVpsInterface flag should be true" Expect.isSome edit.InterfaceAddress "The interface address should exist" Expect.equal edit.InterfaceAddress church.InterfaceAddress "The interface address was not filled correctly" } @@ -206,7 +205,7 @@ let editChurchTests = Expect.equal church.Name edit.Name "The church name was not updated correctly" Expect.equal church.City edit.City "The church's city was not updated correctly" Expect.equal church.State edit.State "The church's state was not updated correctly" - Expect.isTrue church.HasInterface "The church should show that it has an interface" + Expect.isTrue church.HasVpsInterface "The church should show that it has an interface" Expect.isSome church.InterfaceAddress "The interface address should exist" Expect.equal church.InterfaceAddress edit.InterfaceAddress "The interface address was not updated correctly" } @@ -217,7 +216,7 @@ let editChurchTests = City = "Testerville" State = "TE" }.PopulateChurch Church.empty - Expect.isFalse church.HasInterface "The church should show that it has an interface" + Expect.isFalse church.HasVpsInterface "The church should show that it has an interface" Expect.isNone church.InterfaceAddress "The interface address should exist" } ] @@ -284,7 +283,7 @@ let editPreferencesTests = Expect.equal edit.TimeZone (TimeZoneId.toString prefs.TimeZoneId) "The time zone was not filled correctly" Expect.isSome edit.GroupPassword "The group password should have been set" Expect.equal edit.GroupPassword (Some prefs.GroupPassword) "The group password was not filled correctly" - Expect.equal edit.Visibility RequestVisibility.``private`` + Expect.equal edit.Visibility GroupVisibility.PrivateList "The list visibility was not derived correctly" Expect.equal edit.PageSize prefs.PageSize "The page size was not filled correctly" Expect.equal edit.AsOfDate (AsOfDateDisplay.toCode prefs.AsOfDateDisplay) @@ -297,7 +296,7 @@ let editPreferencesTests = Expect.equal edit.LineColor prefs.LineColor "The heading line color was not filled correctly" Expect.isSome edit.GroupPassword "The group password should have been set" Expect.equal edit.GroupPassword (Some prefs.GroupPassword) "The group password was not filled correctly" - Expect.equal edit.Visibility RequestVisibility.passwordProtected + Expect.equal edit.Visibility GroupVisibility.HasPassword "The list visibility was not derived correctly" } test "fromPreferences succeeds for RGB text color and public list" { @@ -307,7 +306,7 @@ let editPreferencesTests = Expect.equal edit.HeadingColor prefs.HeadingColor "The heading text color was not filled correctly" Expect.isSome edit.GroupPassword "The group password should have been set" Expect.equal edit.GroupPassword (Some "") "The group password was not filled correctly" - Expect.equal edit.Visibility RequestVisibility.``public`` + Expect.equal edit.Visibility GroupVisibility.PublicList "The list visibility was not derived correctly" } ] diff --git a/src/PrayerTracker.UI/Church.fs b/src/PrayerTracker.UI/Church.fs index 3d5164f..64d1104 100644 --- a/src/PrayerTracker.UI/Church.fs +++ b/src/PrayerTracker.UI/Church.fs @@ -84,7 +84,7 @@ let maintain (churches : Church list) (stats : Map) ctx vi 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.HasInterface then "Yes" else "No"] ] + td [ _class "pt-center-text" ] [ locStr s[if ch.HasVpsInterface then "Yes" else "No"] ] ]) |> tbody [] ] diff --git a/src/PrayerTracker.UI/Layout.fs b/src/PrayerTracker.UI/Layout.fs index 55baa66..d19e65e 100644 --- a/src/PrayerTracker.UI/Layout.fs +++ b/src/PrayerTracker.UI/Layout.fs @@ -159,7 +159,7 @@ module Navigation = span [ _class "u" ] [ locStr s["Currently Logged On"] ] rawText "   " icon "person" - strong [] [ str u.fullName ] + strong [] [ str u.Name ] rawText "    " | None -> locStr s["Logged On as a Member of"] @@ -169,7 +169,6 @@ module Navigation = match m.User with | Some _ -> a [ _href "/small-group"; Target.content ] [ strong [] [ str g.Name ] ] | None -> strong [] [ str g.Name ] - rawText "  " ] | None -> [] |> div [] @@ -263,12 +262,12 @@ let private htmlFooter viewInfo = let s = I18N.localizer.Force () let imgText = $"""%O{s["PrayerTracker"]} %O{s["from Bit Badger Solutions"]}""" let resultTime = TimeSpan(DateTime.Now.Ticks - viewInfo.RequestStart).TotalSeconds - footer [] [ + footer [ _class "pt-footer" ] [ div [ _id "pt-legal" ] [ a [ _href "/legal/privacy-policy" ] [ locStr s["Privacy Policy"] ] - rawText " • " + rawText "   " a [ _href "/legal/terms-of-service" ] [ locStr s["Terms of Service"] ] - rawText " • " + rawText "   " a [ _href "https://github.com/bit-badger/PrayerTracker" _title s["View source code and get technical support"].Value _target "_blank" @@ -278,7 +277,10 @@ let private htmlFooter viewInfo = ] div [ _id "pt-footer" ] [ a [ _href "/"; _style "line-height:28px;" ] [ - img [ _src $"""/img/%O{s["footer_en"]}.png"""; _alt imgText; _title imgText ] + img [ _src $"""/img/%O{s["footer_en"]}.png""" + _alt imgText + _title imgText + _width "331"; _height "28" ] ] str viewInfo.Version space @@ -286,8 +288,6 @@ let private htmlFooter viewInfo = str "schedule" ] ] - Script.minified - script [ _src "/js/app.js" ] [] ] /// The content portion of the PrayerTracker layout @@ -297,13 +297,21 @@ let private contentSection viewInfo pgTitle (content : XmlNode) = [ yield! messages viewInfo match viewInfo.ScopedStyle with | [] -> () - | styles -> style [ _scoped ] (styles |> List.map (fun it -> rawText $"{it}; ")) + | styles -> style [] [ rawText (styles |> String.concat " ") ] content htmlFooter viewInfo - for jsFile in viewInfo.Script do - script [ _src $"/js/{jsFile}.js" ] [] match viewInfo.OnLoadScript with - | Some onLoad -> script [] [ rawText $"{onLoad}()" ] + | Some onLoad -> + script [] [ + rawText $""" + window.doOnLoad = () => {{ + if (window.PT) {{ + {onLoad}() + delete window.doOnLoad + }} else {{ setTimeout(window.doOnLoad, 500) }} + }} + window.doOnLoad()""" + ] | None -> () ] @@ -323,6 +331,12 @@ let private pageLayout viewInfo pgTitle content = Navigation.top viewInfo div [ _id "pt-body"; Target.content; _hxSwap $"{HxSwap.InnerHtml} show:window:top" ] (contentSection viewInfo pgTitle content) + match viewInfo.Layout with + | FullPage -> + Script.minified + script [ _src "/js/ckeditor/ckeditor.js" ] [] + script [ _src "/js/app.js" ] [] + | _ -> () ] /// The standard layout(s) for PrayerTracker diff --git a/src/PrayerTracker.UI/PrayerRequest.fs b/src/PrayerTracker.UI/PrayerRequest.fs index d89de0f..d118274 100644 --- a/src/PrayerTracker.UI/PrayerRequest.fs +++ b/src/PrayerTracker.UI/PrayerRequest.fs @@ -152,14 +152,14 @@ let maintain (model : MaintainRequests) (ctx : HttpContext) viewInfo = let l = I18N.forView "Requests/Maintain" use sw = new StringWriter () let raw = rawLocText sw - let now = model.SmallGroup.localDateNow (ctx.GetService ()) + let now = model.SmallGroup.LocalDateNow (ctx.GetService ()) let prefs = model.SmallGroup.Preferences let types = ReferenceList.requestTypeList s |> Map.ofList let updReq (req : PrayerRequest) = - if req.updateRequired now prefs.DaysToExpire prefs.LongTermUpdateWeeks then "pt-request-update" else "" + if req.UpdateRequired now prefs.DaysToExpire prefs.LongTermUpdateWeeks then "pt-request-update" else "" |> _class let reqExp (req : PrayerRequest) = - _class (if req.isExpired now prefs.DaysToExpire then "pt-request-expired" else "") + _class (if req.IsExpired now prefs.DaysToExpire then "pt-request-expired" else "") /// Iterate the sequence once, before we render, so we can get the count of it at the top of the table let requests = model.Requests @@ -180,7 +180,7 @@ let maintain (model : MaintainRequests) (ctx : HttpContext) viewInfo = a [ _href $"/prayer-request/{reqId}/edit"; _title l["Edit This Prayer Request"].Value ] [ icon "edit" ] - if req.isExpired now prefs.DaysToExpire then + if req.IsExpired now prefs.DaysToExpire then a [ _href $"/prayer-request/{reqId}/restore" _title l["Restore This Inactive Request"].Value ] [ icon "visibility" @@ -305,39 +305,40 @@ let view model viewInfo = let pageTitle = $"""{s["Prayer Requests"].Value} • {model.SmallGroup.Name}""" let spacer = rawText "       " let dtString = model.Date.ToString "yyyy-MM-dd" - div [ _class "pt-center-text" ] [ - br [] - a [ _class "pt-icon-link" - _href $"/prayer-requests/print/{dtString}" - _target "_blank" - _title s["View Printable"].Value ] [ - icon "print"; rawText "  "; locStr s["View Printable"] - ] - if model.CanEmail then - spacer - if model.Date.DayOfWeek <> DayOfWeek.Sunday then - let rec findSunday (date : DateTime) = - if date.DayOfWeek = DayOfWeek.Sunday then date else findSunday (date.AddDays 1.) - let sunday = findSunday model.Date - a [ _class "pt-icon-link" - _href $"""/prayer-requests/view/{sunday.ToString "yyyy-MM-dd"}""" - _title s["List for Next Sunday"].Value ] [ - icon "update"; rawText "  "; locStr s["List for Next Sunday"] - ] - spacer - let emailPrompt = s["This will e-mail the current list to every member of your group, without further prompting. Are you sure this is what you are ready to do?"].Value - a [ _class "pt-icon-link" - _href $"/prayer-requests/email/{dtString}" - _title s["Send via E-mail"].Value - _onclick $"return PT.requests.view.promptBeforeEmail('{emailPrompt}')" ] [ - icon "mail_outline"; rawText "  "; locStr s["Send via E-mail"] + [ div [ _class "pt-center-text" ] [ + br [] + a [ _class "pt-icon-link" + _href $"/prayer-requests/print/{dtString}" + _target "_blank" + _title s["View Printable"].Value ] [ + icon "print"; rawText "  "; locStr s["View Printable"] + ] + if model.CanEmail then + spacer + if model.Date.DayOfWeek <> DayOfWeek.Sunday then + let rec findSunday (date : DateTime) = + if date.DayOfWeek = DayOfWeek.Sunday then date else findSunday (date.AddDays 1.) + let sunday = findSunday model.Date + a [ _class "pt-icon-link" + _href $"""/prayer-requests/view/{sunday.ToString "yyyy-MM-dd"}""" + _title s["List for Next Sunday"].Value ] [ + icon "update"; rawText "  "; locStr s["List for Next Sunday"] + ] + spacer + let emailPrompt = s["This will e-mail the current list to every member of your group, without further prompting. Are you sure this is what you are ready to do?"].Value + a [ _class "pt-icon-link" + _href $"/prayer-requests/email/{dtString}" + _title s["Send via E-mail"].Value + _onclick $"return PT.requests.view.promptBeforeEmail('{emailPrompt}')" ] [ + icon "mail_outline"; rawText "  "; locStr s["Send via E-mail"] + ] + spacer + a [ _class "pt-icon-link"; _href "/prayer-requests"; _title s["Maintain Prayer Requests"].Value ] [ + icon "compare_arrows"; rawText "  "; locStr s["Maintain Prayer Requests"] ] - spacer - a [ _class "pt-icon-link"; _href "/prayer-requests"; _title s["Maintain Prayer Requests"].Value ] [ - icon "compare_arrows"; rawText "  "; locStr s["Maintain Prayer Requests"] ] + br [] + rawText (model.AsHtml s) ] - |> List.singleton - |> List.append [ br []; rawText (model.AsHtml s) ] |> Layout.Content.standard |> Layout.standard viewInfo pageTitle diff --git a/src/PrayerTracker.UI/SmallGroup.fs b/src/PrayerTracker.UI/SmallGroup.fs index 0faa9f5..f5f3c9a 100644 --- a/src/PrayerTracker.UI/SmallGroup.fs +++ b/src/PrayerTracker.UI/SmallGroup.fs @@ -349,200 +349,199 @@ let preferences (model : EditPreferences) (tzs : TimeZone list) ctx viewInfo = $"@media screen and (max-width: 40rem) {{ {fontsId} {{ width: 100%%; }} }}" ] |> AppViewInfo.withOnLoadScript "PT.smallGroup.preferences.onPageLoad" - form [ _action "/small-group/preferences/save"; _method "post"; _class "pt-center-columns"; Target.content ] [ - csrfToken ctx - fieldset [] [ - legend [] [ strong [] [ icon "date_range"; rawText "  "; locStr s["Dates"] ] ] - div [ _fieldRow ] [ - div [ _inputField ] [ - label [ _for (nameof model.ExpireDays) ] [ locStr s["Requests Expire After"] ] - span [] [ - inputField "number" (nameof model.ExpireDays) (string model.ExpireDays) [ - _min "1"; _max "30"; _required; _autofocus + [ form [ _action "/small-group/preferences/save"; _method "post"; _class "pt-center-columns"; Target.content ] [ + csrfToken ctx + fieldset [] [ + legend [] [ strong [] [ icon "date_range"; rawText "  "; locStr s["Dates"] ] ] + div [ _fieldRow ] [ + div [ _inputField ] [ + label [ _for (nameof model.ExpireDays) ] [ locStr s["Requests Expire After"] ] + span [] [ + inputField "number" (nameof model.ExpireDays) (string model.ExpireDays) [ + _min "1"; _max "30"; _required; _autofocus + ] + space + str (s["Days"].Value.ToLower ()) ] - space - str (s["Days"].Value.ToLower ()) ] - ] - div [ _inputField ] [ - label [ _for (nameof model.DaysToKeepNew) ] [ locStr s["Requests “New” For"] ] - span [] [ - inputField "number" (nameof model.DaysToKeepNew) (string model.DaysToKeepNew) [ - _min "1"; _max "30"; _required + div [ _inputField ] [ + label [ _for (nameof model.DaysToKeepNew) ] [ locStr s["Requests “New” For"] ] + span [] [ + inputField "number" (nameof model.DaysToKeepNew) (string model.DaysToKeepNew) [ + _min "1"; _max "30"; _required + ] + space; str (s["Days"].Value.ToLower ()) ] - space; str (s["Days"].Value.ToLower ()) ] - ] - div [ _inputField ] [ - label [ _for (nameof model.LongTermUpdateWeeks) ] [ - locStr s["Long-Term Requests Alerted for Update"] - ] - span [] [ - inputField "number" (nameof model.LongTermUpdateWeeks) (string model.LongTermUpdateWeeks) [ - _min "1"; _max "30"; _required + div [ _inputField ] [ + label [ _for (nameof model.LongTermUpdateWeeks) ] [ + locStr s["Long-Term Requests Alerted for Update"] + ] + span [] [ + inputField "number" (nameof model.LongTermUpdateWeeks) (string model.LongTermUpdateWeeks) [ + _min "1"; _max "30"; _required + ] + space; str (s["Weeks"].Value.ToLower ()) ] - space; str (s["Weeks"].Value.ToLower ()) ] ] ] - ] - fieldset [] [ - legend [] [ strong [] [ icon "sort"; rawText "  "; locStr s["Request Sorting"] ] ] - radio (nameof model.RequestSort) $"{nameof model.RequestSort}_D" "D" model.RequestSort - label [ _for $"{nameof model.RequestSort}_D" ] [ locStr s["Sort by Last Updated Date"] ] - rawText "   " - radio (nameof model.RequestSort) $"{nameof model.RequestSort}_R" "R" model.RequestSort - label [ _for $"{nameof model.RequestSort}_R" ] [ locStr s["Sort by Requestor Name"] ] - ] - fieldset [] [ - legend [] [ strong [] [ icon "mail_outline"; rawText "  "; locStr s["E-mail"] ] ] - div [ _fieldRow ] [ - div [ _inputField ] [ - label [ _for (nameof model.EmailFromName) ] [ locStr s["From Name"] ] - inputField "text" (nameof model.EmailFromName) model.EmailFromName [ _required ] + fieldset [] [ + legend [] [ strong [] [ icon "sort"; rawText "  "; locStr s["Request Sorting"] ] ] + radio (nameof model.RequestSort) $"{nameof model.RequestSort}_D" "D" model.RequestSort + label [ _for $"{nameof model.RequestSort}_D" ] [ locStr s["Sort by Last Updated Date"] ] + rawText "   " + radio (nameof model.RequestSort) $"{nameof model.RequestSort}_R" "R" model.RequestSort + label [ _for $"{nameof model.RequestSort}_R" ] [ locStr s["Sort by Requestor Name"] ] + ] + fieldset [] [ + legend [] [ strong [] [ icon "mail_outline"; rawText "  "; locStr s["E-mail"] ] ] + div [ _fieldRow ] [ + div [ _inputField ] [ + label [ _for (nameof model.EmailFromName) ] [ locStr s["From Name"] ] + inputField "text" (nameof model.EmailFromName) model.EmailFromName [ _required ] + ] + div [ _inputField ] [ + label [ _for (nameof model.EmailFromAddress) ] [ locStr s["From Address"] ] + inputField "email" (nameof model.EmailFromAddress) model.EmailFromAddress [ _required ] + ] ] - div [ _inputField ] [ - label [ _for (nameof model.EmailFromAddress) ] [ locStr s["From Address"] ] - inputField "email" (nameof model.EmailFromAddress) model.EmailFromAddress [ _required ] + div [ _fieldRow ] [ + div [ _inputField ] [ + label [ _for (nameof model.DefaultEmailType) ] [ locStr s["E-mail Format"] ] + seq { + "", selectDefault s["Select"].Value + yield! + ReferenceList.emailTypeList HtmlFormat s + |> Seq.skip 1 + |> Seq.map (fun typ -> fst typ, (snd typ).Value) + } + |> selectList (nameof model.DefaultEmailType) model.DefaultEmailType [ _required ] + ] ] ] - div [ _fieldRow ] [ - div [ _inputField ] [ - label [ _for (nameof model.DefaultEmailType) ] [ locStr s["E-mail Format"] ] - seq { - "", selectDefault s["Select"].Value - yield! - ReferenceList.emailTypeList HtmlFormat s - |> Seq.skip 1 - |> Seq.map (fun typ -> fst typ, (snd typ).Value) - } - |> selectList (nameof model.DefaultEmailType) model.DefaultEmailType [ _required ] + fieldset [] [ + legend [] [ strong [] [ icon "color_lens"; rawText "  "; locStr s["Colors"] ]; rawText " ***" ] + div [ _fieldRow ] [ + div [ _inputField ] [ + label [ _class "pt-center-text" ] [ locStr s["Color of Heading Lines"] ] + span [] [ + radio (nameof model.LineColorType) $"{nameof model.LineColorType}_Name" "Name" + model.LineColorType + label [ _for $"{nameof model.LineColorType}_Name" ] [ locStr s["Named Color"] ] + namedColorList (nameof model.LineColor) model.LineColor [ + _id $"{nameof model.LineColor}_Select" + if model.LineColor.StartsWith "#" then _disabled ] s + rawText "    "; str (s["or"].Value.ToUpper ()) + radio (nameof model.LineColorType) $"{nameof model.LineColorType}_RGB" "RGB" + model.LineColorType + label [ _for $"{nameof model.LineColorType}_RGB" ] [ locStr s["Custom Color"] ] + input [ _type "color" + _name (nameof model.LineColor) + _id $"{nameof model.LineColor}_Color" + _value model.LineColor // TODO: convert to hex or skip if named + if not (model.LineColor.StartsWith "#") then _disabled ] + ] + ] + ] + div [ _fieldRow ] [ + div [ _inputField ] [ + label [ _class "pt-center-text" ] [ locStr s["Color of Heading Text"] ] + span [] [ + radio (nameof model.HeadingColorType) $"{nameof model.HeadingColorType}_Name" "Name" + model.HeadingColorType + label [ _for $"{nameof model.HeadingColorType}_Name" ] [ locStr s["Named Color"] ] + namedColorList (nameof model.HeadingColor) model.HeadingColor [ + _id $"{nameof model.HeadingColor}_Select" + if model.HeadingColor.StartsWith "#" then _disabled ] s + rawText "    "; str (s["or"].Value.ToUpper ()) + radio (nameof model.HeadingColorType) $"{nameof model.HeadingColorType}_RGB" "RGB" + model.HeadingColorType + label [ _for $"{nameof model.HeadingColorType}_RGB" ] [ locStr s["Custom Color"] ] + input [ _type "color" + _name (nameof model.HeadingColor) + _id $"{nameof model.HeadingColor}_Color" + _value model.HeadingColor // TODO: convert to hex or skip if named + if not (model.HeadingColor.StartsWith "#") then _disabled ] + ] + ] ] ] - ] - fieldset [] [ - legend [] [ strong [] [ icon "color_lens"; rawText "  "; locStr s["Colors"] ]; rawText " ***" ] - div [ _fieldRow ] [ + fieldset [] [ + legend [] [ strong [] [ icon "font_download"; rawText "  "; locStr s["Fonts"] ] ] div [ _inputField ] [ - label [ _class "pt-center-text" ] [ locStr s["Color of Heading Lines"] ] + label [ _for (nameof model.Fonts) ] [ locStr s["Fonts** for List"] ] + inputField "text" (nameof model.Fonts) model.Fonts [ _required ] + ] + div [ _fieldRow ] [ + div [ _inputField ] [ + label [ _for (nameof model.HeadingFontSize) ] [ locStr s["Heading Text Size"] ] + inputField "number" (nameof model.HeadingFontSize) (string model.HeadingFontSize) [ + _min "8"; _max "24"; _required + ] + ] + div [ _inputField ] [ + label [ _for (nameof model.ListFontSize) ] [ locStr s["List Text Size"] ] + inputField "number" (nameof model.ListFontSize) (string model.ListFontSize) [ + _min "8"; _max "24"; _required + ] + ] + ] + ] + fieldset [] [ + legend [] [ strong [] [ icon "settings"; rawText "  "; locStr s["Other Settings"] ] ] + div [ _fieldRow ] [ + div [ _inputField ] [ + label [ _for (nameof model.TimeZone) ] [ locStr s["Time Zone"] ] + seq { + "", selectDefault s["Select"].Value + yield! + tzs + |> List.map (fun tz -> + TimeZoneId.toString tz.Id, (TimeZones.name tz.Id s).Value) + } + |> selectList (nameof model.TimeZone) model.TimeZone [ _required ] + ] + ] + div [ _inputField ] [ + label [] [ locStr s["Request List Visibility"] ] span [] [ - radio (nameof model.LineColorType) $"{nameof model.LineColorType}_Name" "Name" - model.LineColorType - label [ _for $"{nameof model.LineColorType}_Name" ] [ locStr s["Named Color"] ] - namedColorList (nameof model.LineColor) model.LineColor [ - _id $"{nameof model.LineColor}_Select" - if model.LineColor.StartsWith "#" then _disabled ] s - rawText "    "; str (s["or"].Value.ToUpper ()) - radio (nameof model.LineColorType) $"{nameof model.LineColorType}_RGB" "RGB" model.LineColorType - label [ _for $"{nameof model.LineColorType}_RGB" ] [ locStr s["Custom Color"] ] - input [ _type "color" - _name (nameof model.LineColor) - _id $"{nameof model.LineColor}_Color" - _value model.LineColor // TODO: convert to hex or skip if named - if not (model.LineColor.StartsWith "#") then _disabled ] - ] - ] - ] - div [ _fieldRow ] [ - div [ _inputField ] [ - label [ _class "pt-center-text" ] [ locStr s["Color of Heading Text"] ] - span [] [ - radio (nameof model.HeadingColorType) $"{nameof model.HeadingColorType}_Name" "Name" - model.HeadingColorType - label [ _for $"{nameof model.HeadingColorType}_Name" ] [ locStr s["Named Color"] ] - namedColorList (nameof model.HeadingColor) model.HeadingColor [ - _id $"{nameof model.HeadingColor}_Select" - if model.HeadingColor.StartsWith "#" then _disabled ] s - rawText "    "; str (s["or"].Value.ToUpper ()) - radio (nameof model.HeadingColorType) $"{nameof model.HeadingColorType}_RGB" "RGB" - model.HeadingColorType - label [ _for $"{nameof model.HeadingColorType}_RGB" ] [ locStr s["Custom Color"] ] - input [ _type "color" - _name (nameof model.HeadingColor) - _id $"{nameof model.HeadingColor}_Color" - _value model.HeadingColor // TODO: convert to hex or skip if named - if not (model.HeadingColor.StartsWith "#") then _disabled ] + let name = nameof model.Visibility + let value = string model.Visibility + radio name $"{name}_Public" (string GroupVisibility.PublicList) value + label [ _for $"{name}_Public" ] [ locStr s["Public"] ] + rawText "  " + radio name $"{name}_Private" (string GroupVisibility.PrivateList) value + label [ _for $"{name}_Private" ] [ locStr s["Private"] ] + rawText "  " + radio name $"{name}_Password" (string GroupVisibility.HasPassword) value + label [ _for $"{name}_Password" ] [ locStr s["Password Protected"] ] + ] + ] + let classSuffix = if model.Visibility = GroupVisibility.HasPassword then [ "pt-show" ] else [] + div [ _id "divClassPassword"; _fieldRowWith ("pt-fadeable" :: classSuffix) ] [ + div [ _inputField ] [ + label [ _for (nameof model.GroupPassword) ] [ locStr s["Group Password (Used to Read Online)"] ] + inputField "text" (nameof model.GroupPassword) (defaultArg model.GroupPassword "") [] + ] + ] + div [ _fieldRow ] [ + div [ _inputField ] [ + label [ _for (nameof model.PageSize) ] [ locStr s["Page Size"] ] + inputField "number" (nameof model.PageSize) (string model.PageSize) [ + _min "10"; _max "255"; _required + ] + ] + div [ _inputField ] [ + label [ _for (nameof model.AsOfDate) ] [ locStr s["“As of” Date Display"] ] + ReferenceList.asOfDateList s + |> List.map (fun (code, desc) -> code, desc.Value) + |> selectList (nameof model.AsOfDate) model.AsOfDate [ _required ] ] ] ] + div [ _fieldRow ] [ submit [] "save" s["Save Preferences"] ] ] - fieldset [] [ - legend [] [ strong [] [ icon "font_download"; rawText "  "; locStr s["Fonts"] ] ] - div [ _inputField ] [ - label [ _for (nameof model.Fonts) ] [ locStr s["Fonts** for List"] ] - inputField "text" (nameof model.Fonts) model.Fonts [ _required ] - ] - div [ _fieldRow ] [ - div [ _inputField ] [ - label [ _for (nameof model.HeadingFontSize) ] [ locStr s["Heading Text Size"] ] - inputField "number" (nameof model.HeadingFontSize) (string model.HeadingFontSize) [ - _min "8"; _max "24"; _required - ] - ] - div [ _inputField ] [ - label [ _for (nameof model.ListFontSize) ] [ locStr s["List Text Size"] ] - inputField "number" (nameof model.ListFontSize) (string model.ListFontSize) [ - _min "8"; _max "24"; _required - ] - ] - ] - ] - fieldset [] [ - legend [] [ strong [] [ icon "settings"; rawText "  "; locStr s["Other Settings"] ] ] - div [ _fieldRow ] [ - div [ _inputField ] [ - label [ _for (nameof model.TimeZone) ] [ locStr s["Time Zone"] ] - seq { - "", selectDefault s["Select"].Value - yield! - tzs - |> List.map (fun tz -> - TimeZoneId.toString tz.Id, (TimeZones.name tz.Id s).Value) - } - |> selectList (nameof model.TimeZone) model.TimeZone [ _required ] - ] - ] - div [ _inputField ] [ - label [] [ locStr s["Request List Visibility"] ] - span [] [ - let name = nameof model.Visibility - let value = string model.Visibility - radio name $"{name}_Public" (string RequestVisibility.``public``) value - label [ _for $"{name}_Public" ] [ locStr s["Public"] ] - rawText "  " - radio name $"{name}_Private" (string RequestVisibility.``private``) value - label [ _for $"{name}_Private" ] [ locStr s["Private"] ] - rawText "  " - radio name $"{name}_Password" (string RequestVisibility.passwordProtected) value - label [ _for $"{name}_Password" ] [ locStr s["Password Protected"] ] - ] - ] - let classSuffix = if model.Visibility = RequestVisibility.passwordProtected then [ "pt-show" ] else [] - div [ _id "divClassPassword"; _fieldRowWith ("pt-fadeable" :: classSuffix) ] [ - div [ _inputField ] [ - label [ _for (nameof model.GroupPassword) ] [ locStr s["Group Password (Used to Read Online)"] ] - inputField "text" (nameof model.GroupPassword) (defaultArg model.GroupPassword "") [] - ] - ] - div [ _fieldRow ] [ - div [ _inputField ] [ - label [ _for (nameof model.PageSize) ] [ locStr s["Page Size"] ] - inputField "number" (nameof model.PageSize) (string model.PageSize) [ - _min "10"; _max "255"; _required - ] - ] - div [ _inputField ] [ - label [ _for (nameof model.AsOfDate) ] [ locStr s["“As of” Date Display"] ] - ReferenceList.asOfDateList s - |> List.map (fun (code, desc) -> code, desc.Value) - |> selectList (nameof model.AsOfDate) model.AsOfDate [ _required ] - ] - ] - ] - div [ _fieldRow ] [ submit [] "save" s["Save Preferences"] ] - ] - |> List.singleton - |> List.append [ p [] [ rawText "** " raw l["List font names, separated by commas."] diff --git a/src/PrayerTracker.UI/User.fs b/src/PrayerTracker.UI/User.fs index 3de292e..a6321e5 100644 --- a/src/PrayerTracker.UI/User.fs +++ b/src/PrayerTracker.UI/User.fs @@ -199,7 +199,7 @@ let maintain (users : User list) ctx viewInfo = let userId = shortGuid user.Id.Value let delAction = $"/user/{userId}/delete" let delPrompt = s["Are you sure you want to delete this {0}? This action cannot be undone.", - $"""{s["User"].Value.ToLower ()} ({user.fullName})"""].Value + $"""{s["User"].Value.ToLower ()} ({user.Name})"""].Value tr [] [ td [] [ a [ _href $"/user/{userId}/edit"; _title s["Edit This User"].Value ] [ icon "edit" ] @@ -212,7 +212,7 @@ let maintain (users : User list) ctx viewInfo = icon "delete_forever" ] ] - td [] [ str user.fullName ] + td [] [ str user.Name ] td [ _class "pt-center-text" ] [ if user.IsAdmin then strong [] [ locStr s["Yes"] ] else locStr s["No"] ] diff --git a/src/PrayerTracker.UI/Utils.fs b/src/PrayerTracker.UI/Utils.fs index 901937c..bd9abde 100644 --- a/src/PrayerTracker.UI/Utils.fs +++ b/src/PrayerTracker.UI/Utils.fs @@ -175,19 +175,19 @@ module Key = /// Enumerated values for small group request list visibility (derived from preferences, used in UI) -module RequestVisibility = +module GroupVisibility = /// Requests are publicly accessible [] - let ``public`` = 1 + let PublicList = 1 /// The small group members can enter a password to view the request list [] - let passwordProtected = 2 + let HasPassword = 2 /// No one can see the requests for a small group except its administrators ("User" access level) [] - let ``private`` = 3 + let PrivateList = 3 /// Links for help locations diff --git a/src/PrayerTracker.UI/ViewModels.fs b/src/PrayerTracker.UI/ViewModels.fs index 9a0c8ed..dce8c95 100644 --- a/src/PrayerTracker.UI/ViewModels.fs +++ b/src/PrayerTracker.UI/ViewModels.fs @@ -124,9 +124,6 @@ type AppViewInfo = { /// CSS files for the page Style : string list - /// JavaScript files for the page - Script : string list - /// The link for help on this page HelpLink : string option @@ -162,7 +159,6 @@ module AppViewInfo = /// A fresh version that can be populated to process the current request let fresh = { Style = [] - Script = [] HelpLink = None Messages = [] Version = "" @@ -224,7 +220,7 @@ module AssignGroups = /// Create an instance of this form from an existing user let fromUser (user : User) = { UserId = shortGuid user.Id.Value - UserName = user.fullName + UserName = user.Name SmallGroups = "" } @@ -275,7 +271,7 @@ with Name = this.Name City = this.City State = this.State - HasInterface = match this.HasInterface with Some x -> x | None -> false + HasVpsInterface = match this.HasInterface with Some x -> x | None -> false InterfaceAddress = match this.HasInterface with Some x when x -> this.InterfaceAddress | _ -> None } @@ -288,7 +284,7 @@ module EditChurch = Name = church.Name City = church.City State = church.State - HasInterface = match church.HasInterface with true -> Some true | false -> None + HasInterface = match church.HasVpsInterface with true -> Some true | false -> None InterfaceAddress = church.InterfaceAddress } @@ -408,11 +404,9 @@ with /// Set the properties of a small group based on the form's properties member this.PopulatePreferences (prefs : ListPreferences) = let isPublic, grpPw = - match this.Visibility with - | RequestVisibility.``public`` -> true, "" - | RequestVisibility.passwordProtected -> false, (defaultArg this.GroupPassword "") - | RequestVisibility.``private`` - | _ -> false, "" + if this.Visibility = GroupVisibility.PublicList then true, "" + elif this.Visibility = GroupVisibility.HasPassword then false, (defaultArg this.GroupPassword "") + else (* GroupVisibility.PrivateList *) false, "" { prefs with DaysToExpire = this.ExpireDays DaysToKeepNew = this.DaysToKeepNew @@ -457,10 +451,9 @@ module EditPreferences = PageSize = prefs.PageSize AsOfDate = AsOfDateDisplay.toCode prefs.AsOfDateDisplay Visibility = - match true with - | _ when prefs.IsPublic -> RequestVisibility.``public`` - | _ when prefs.GroupPassword = "" -> RequestVisibility.``private`` - | _ -> RequestVisibility.passwordProtected + if prefs.IsPublic then GroupVisibility.PublicList + elif prefs.GroupPassword = "" then GroupVisibility.PrivateList + else GroupVisibility.HasPassword } diff --git a/src/PrayerTracker/PrayerRequest.fs b/src/PrayerTracker/PrayerRequest.fs index 8deb1bd..8ea9c7d 100644 --- a/src/PrayerTracker/PrayerRequest.fs +++ b/src/PrayerTracker/PrayerRequest.fs @@ -1,10 +1,7 @@ module PrayerTracker.Handlers.PrayerRequest -open System -open System.Threading.Tasks open Giraffe open Microsoft.AspNetCore.Http -open NodaTime open PrayerTracker open PrayerTracker.Entities open PrayerTracker.ViewModels @@ -20,11 +17,13 @@ let private findRequest (ctx : HttpContext) reqId = task { | None -> return Result.Error fourOhFour } +open NodaTime + /// Generate a list of requests for the given date let private generateRequestList ctx date = task { let grp = currentGroup ctx let clock = ctx.GetService () - let listDate = match date with Some d -> d | None -> grp.localDateNow clock + let listDate = match date with Some d -> d | None -> grp.LocalDateNow clock let! reqs = ctx.db.AllRequestsForSmallGroup grp clock (Some listDate) true 0 return { Requests = reqs @@ -36,29 +35,30 @@ let private generateRequestList ctx date = task { } } +open System + /// Parse a string into a date (optionally, of course) let private parseListDate (date : string option) = match date with | Some dt -> match DateTime.TryParse dt with true, d -> Some d | false, _ -> None | None -> None - /// GET /prayer-request/[request-id]/edit let edit reqId : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task { let startTicks = DateTime.Now.Ticks let grp = currentGroup ctx - let now = grp.localDateNow (ctx.GetService ()) + let now = grp.LocalDateNow (ctx.GetService ()) let requestId = PrayerRequestId reqId if requestId.Value = Guid.Empty then return! - { viewInfo ctx startTicks with Script = [ "ckeditor/ckeditor" ]; HelpLink = Some Help.editRequest } + { viewInfo ctx startTicks with HelpLink = Some Help.editRequest } |> Views.PrayerRequest.edit EditRequest.empty (now.ToString "yyyy-MM-dd") ctx |> renderHtml next ctx else match! findRequest ctx requestId with | Ok req -> let s = Views.I18N.localizer.Force () - if req.isExpired now grp.Preferences.DaysToExpire then + if req.IsExpired now grp.Preferences.DaysToExpire then { UserMessage.warning with Text = htmlLocString s["This request is expired."] Description = @@ -68,13 +68,12 @@ let edit reqId : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task { } |> addUserMessage ctx return! - { viewInfo ctx startTicks with Script = [ "ckeditor/ckeditor" ]; HelpLink = Some Help.editRequest } + { viewInfo ctx startTicks with HelpLink = Some Help.editRequest } |> Views.PrayerRequest.edit (EditRequest.fromRequest req) "" ctx |> renderHtml next ctx | Result.Error e -> return! e next ctx } - /// GET /prayer-requests/email/[date] let email date : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task { let startTicks = DateTime.Now.Ticks @@ -93,7 +92,6 @@ let email date : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task { |> renderHtml next ctx } - /// POST /prayer-request/[request-id]/delete let delete reqId : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ctx -> task { let requestId = PrayerRequestId reqId @@ -107,7 +105,6 @@ let delete reqId : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun | Result.Error e -> return! e next ctx } - /// GET /prayer-request/[request-id]/expire let expire reqId : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task { let requestId = PrayerRequestId reqId @@ -121,7 +118,6 @@ let expire reqId : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task | Result.Error e -> return! e next ctx } - /// GET /prayer-requests/[group-id]/list let list groupId : HttpHandler = requireAccess [ AccessLevel.Public ] >=> fun next ctx -> task { let startTicks = DateTime.Now.Ticks @@ -132,12 +128,12 @@ let list groupId : HttpHandler = requireAccess [ AccessLevel.Public ] >=> fun ne return! viewInfo ctx startTicks |> Views.PrayerRequest.list - { Requests = reqs - Date = grp.localDateNow clock - SmallGroup = grp - ShowHeader = true - CanEmail = Option.isSome ctx.Session.user - Recipients = [] + { Requests = reqs + Date = grp.LocalDateNow clock + SmallGroup = grp + ShowHeader = true + CanEmail = Option.isSome ctx.Session.user + Recipients = [] } |> renderHtml next ctx | Some _ -> @@ -147,7 +143,6 @@ let list groupId : HttpHandler = requireAccess [ AccessLevel.Public ] >=> fun ne | None -> return! fourOhFour next ctx } - /// GET /prayer-requests/lists let lists : HttpHandler = requireAccess [ AccessLevel.Public ] >=> fun next ctx -> task { let startTicks = DateTime.Now.Ticks @@ -158,7 +153,6 @@ let lists : HttpHandler = requireAccess [ AccessLevel.Public ] >=> fun next ctx |> renderHtml next ctx } - /// GET /prayer-requests[/inactive?] /// - OR - /// GET /prayer-requests?search=[search-query] @@ -201,8 +195,6 @@ let print date : HttpHandler = requireAccess [ User; Group ] >=> fun next ctx -> Views.PrayerRequest.print list appVersion |> renderHtml next ctx } - - /// GET /prayer-request/[request-id]/restore let restore reqId : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task { @@ -217,6 +209,7 @@ let restore reqId : HttpHandler = requireAccess [ User ] >=> fun next ctx -> tas | Result.Error e -> return! e next ctx } +open System.Threading.Tasks /// POST /prayer-request/save let save : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ctx -> task { @@ -235,7 +228,7 @@ let save : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ct Expiration = Expiration.fromCode m.Expiration } let grp = currentGroup ctx - let now = grp.localDateNow (ctx.GetService ()) + let now = grp.LocalDateNow (ctx.GetService ()) match m.IsNew with | true -> let dt = defaultArg m.EnteredDate now @@ -257,7 +250,6 @@ let save : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ct | Result.Error e -> return! bindError e next ctx } - /// GET /prayer-request/view/[date?] let view date : HttpHandler = requireAccess [ User; Group ] >=> fun next ctx -> task { let startTicks = DateTime.Now.Ticks diff --git a/src/PrayerTracker/SmallGroup.fs b/src/PrayerTracker/SmallGroup.fs index 26dc411..d97ce28 100644 --- a/src/PrayerTracker/SmallGroup.fs +++ b/src/PrayerTracker/SmallGroup.fs @@ -16,7 +16,7 @@ let private setGroupCookie (ctx : HttpContext) pwHash = /// GET /small-group/announcement let announcement : HttpHandler = requireAccess [ User ] >=> fun next ctx -> - { viewInfo ctx DateTime.Now.Ticks with HelpLink = Some Help.sendAnnouncement; Script = [ "ckeditor/ckeditor" ] } + { viewInfo ctx DateTime.Now.Ticks with HelpLink = Some Help.sendAnnouncement } |> Views.SmallGroup.announcement (currentUser ctx).IsAdmin ctx |> renderHtml next ctx @@ -266,7 +266,7 @@ let sendAnnouncement : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> | Ok m -> let grp = currentGroup ctx let usr = currentUser ctx - let now = grp.localTimeNow (ctx.GetService ()) + let now = grp.LocalTimeNow (ctx.GetService ()) let s = Views.I18N.localizer.Force () // Reformat the text to use the class's font stylings let requestText = ckEditorToText m.Text diff --git a/src/PrayerTracker/User.fs b/src/PrayerTracker/User.fs index 89cd0bb..a5b5775 100644 --- a/src/PrayerTracker/User.fs +++ b/src/PrayerTracker/User.fs @@ -85,7 +85,7 @@ let delete usrId : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fu ctx.db.RemoveEntry user let! _ = ctx.db.SaveChangesAsync () let s = Views.I18N.localizer.Force () - addInfo ctx s["Successfully deleted user {0}", user.fullName] + addInfo ctx s["Successfully deleted user {0}", user.Name] return! redirectTo false "/users" next ctx | _ -> return! fourOhFour next ctx } @@ -220,10 +220,9 @@ let save : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next c if m.IsNew then let h = CommonFunctions.htmlString { UserMessage.info with - Text = h s["Successfully {0} user", s["Added"].Value.ToLower ()] + Text = h s["Successfully {0} user", s["Added"].Value.ToLower ()] Description = - h s["Please select at least one group for which this user ({0}) is authorized", - updatedUser.fullName] + h s["Please select at least one group for which this user ({0}) is authorized", updatedUser.Name] |> Some } |> addUserMessage ctx diff --git a/src/PrayerTracker/wwwroot/css/app.css b/src/PrayerTracker/wwwroot/css/app.css index dfed2dd..5ec927e 100644 --- a/src/PrayerTracker/wwwroot/css/app.css +++ b/src/PrayerTracker/wwwroot/css/app.css @@ -1,10 +1,18 @@ /** * This is the main stylesheet for the PrayerTracker application. */ +:root { + --dark-blue-hue: 240; + --dark-blue-sat: 100%; + --darkest: hsl(var(--dark-blue-hue), var(--dark-blue-sat), 6%); + --dark: hsl(var(--dark-blue-hue), var(--dark-blue-sat), 13%); + --lighter-dark: hsl(var(--dark-blue-hue), var(--dark-blue-sat), 25%); + --inverse-backgroud: hsl(0, 0%, 95%); + --background: hsla(0, 0%, 0%, .01); +} body { - background-color: #222; + background-color: var(--background); margin: 0; - margin-bottom: 25px; font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif; font-size: 1rem; } @@ -18,10 +26,10 @@ a, a:link, a:visited { text-decoration: none; - color: navy; + color: var(--dark); } a:hover { - border-bottom: dotted 1px navy; + border-bottom: dotted 1px var(--darkest); } a > img { border: 0; @@ -32,7 +40,7 @@ a > img { justify-content: space-between; overflow: hidden; width: 100%; - background-image: linear-gradient(to bottom, #222, #444); + background-image: linear-gradient(to bottom, var(--darkest), var(--dark)); margin-bottom: 0; } .pt-title-bar-left, @@ -46,7 +54,7 @@ a > img { float: left; font-size: 1.25rem; font-weight: bold; - padding: .5rem 1rem 0 1rem; + padding: .5rem 1rem 0 .75rem; } .pt-title-bar-home a:link, .pt-title-bar-home a:visited { @@ -87,7 +95,7 @@ a > img { .pt-title-bar .dropdown-content { display: none; position: absolute; - background-image: linear-gradient(to bottom, #444, #888); + background-image: linear-gradient(to bottom, var(--dark), var(--lighter-dark)); min-width: 160px; box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2); z-index: 1; @@ -100,14 +108,14 @@ a > img { text-align: left; } .pt-title-bar .dropdown-content a:hover { - background-color: #222; + background-color: var(--inverse-backgroud); + color: var(--lighter-dark); } .pt-title-bar .dropdown:hover .dropdown-content { display: block; } #pt-body { - background-color: #fcfcfc; - padding-bottom: 10px; + padding-bottom: 1rem; } #pt-language { background-color: lightgray; @@ -116,31 +124,31 @@ a > img { justify-content: space-between; border-bottom: solid 1px darkgray; border-top: solid 1px darkgray; + padding: 0 .75rem; } #pt-page-title { text-align: center; border-bottom: dotted 1px lightgray; } .pt-content { - margin: auto; + margin: auto auto 1.5rem auto; max-width: 60rem; } .pt-content.pt-full-width { max-width: unset; - margin-left: .5%; - margin-right: .5%; + margin-left: .75rem; + margin-right: .75rem; } @media screen and (max-width: 60rem) { .pt-content { - margin-left: .5%; - margin-right: .5%; + margin-left: .75rem; + margin-right: .75rem; } } fieldset { - margin: auto; + margin: auto auto 1rem auto; border: solid 1px #ccc; border-radius: 1rem; - margin-bottom: 1rem; } input[type=email], input[type=text], @@ -148,7 +156,8 @@ input[type=password], input[type=date], input[type=number], select { - border-radius: 5px; + border-radius: .25rem; + border-color: var(--lighter-dark); font-size: 1rem; padding: .2rem; font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif; @@ -160,30 +169,40 @@ button[type=submit] { border-radius: 10px; padding: .2rem 1rem; margin-top: .5rem; - background-color: #444; - border: none; + background-color: var(--lighter-dark); + border: solid 1px var(--lighter-dark); color: white; } button[type=submit]:hover { - background-color: #222; - cursor:pointer; + color: var(--lighter-dark); + background-color: var(--inverse-backgroud); + cursor: pointer; } -footer { +footer.pt-footer { position: fixed; bottom: 0; width: 100%; - border-bottom: solid 10px #222; + padding-top: .5rem; + background-image: linear-gradient(to bottom, var(--background), var(--darkest)); + display: flex; + flex-flow: row wrap; + justify-content: space-between; + align-items: end; } #pt-legal { - background-color: #222; - margin: 0 0 -30px 0; - padding-left: 10px; + padding-left: .75rem; } #pt-legal a:link, #pt-legal a:visited { - color: lightgray; - color: rgba(255, 255, 255, .5); + color: white; font-size: 10pt; + background-color: var(--darkest); + padding: 0 .5rem; + border-top-left-radius: .5rem; + border-top-right-radius: .5rem; +} +#pt-legal a:hover { + background-color: var(--lighter-dark); } #pt-footer { border: solid 2px navy; @@ -191,12 +210,10 @@ footer { border-top-left-radius: 7px; border-top-right-radius: 7px; padding: 2px 5px 0 5px; - margin: 0 10px -11px auto; + margin-right: .75rem; font-size: 70%; color: navy; background-color: #eee; - float:right; - vertical-align: bottom; } #pt-footer img { vertical-align: bottom; @@ -300,7 +317,7 @@ article.pt-overview section header { text-align: center; border-top-left-radius: 1rem; border-top-right-radius: 1rem; - background-image: linear-gradient(to bottom, #444, #888); + background-image: linear-gradient(to bottom, var(--dark), var(--lighter-dark)); padding: .5rem 1rem; color: white; } @@ -308,7 +325,7 @@ article.pt-overview section div { padding: .5rem; } article.pt-overview section div hr { - color: #444; + color: var(--dark); margin: .5rem -.5rem; } article.pt-overview section div p { diff --git a/src/names-to-lower.sql b/src/names-to-lower.sql new file mode 100644 index 0000000..1a33ae3 --- /dev/null +++ b/src/names-to-lower.sql @@ -0,0 +1,106 @@ +-- Church +ALTER TABLE pt."Church" RENAME COLUMN "ChurchId" TO id; +ALTER TABLE pt."Church" RENAME COLUMN "Name" TO church_name; +ALTER TABLE pt."Church" RENAME COLUMN "City" TO city; +ALTER TABLE pt."Church" RENAME COLUMN "ST" TO state; +ALTER TABLE pt."Church" RENAME COLUMN "HasVirtualPrayerRoomInterface" TO has_vps_interface; +ALTER TABLE pt."Church" RENAME COLUMN "InterfaceAddress" TO interface_address; +ALTER TABLE pt."Church" RENAME CONSTRAINT "PK_Church" TO pk_church; +ALTER TABLE pt."Church" RENAME TO church; + +-- List Preference +ALTER TABLE pt."ListPreference" RENAME COLUMN "SmallGroupId" TO small_group_id; +ALTER TABLE pt."ListPreference" RENAME COLUMN "DaysToExpire" TO days_to_expire; +ALTER TABLE pt."ListPreference" RENAME COLUMN "DaysToKeepNew" TO days_to_keep_new; +ALTER TABLE pt."ListPreference" RENAME COLUMN "LongTermUpdateWeeks" TO long_term_update_weeks; +ALTER TABLE pt."ListPreference" RENAME COLUMN "EmailFromName" TO email_from_name; +ALTER TABLE pt."ListPreference" RENAME COLUMN "EmailFromAddress" TO email_from_address; +ALTER TABLE pt."ListPreference" RENAME COLUMN "ListFonts" TO fonts; +ALTER TABLE pt."ListPreference" RENAME COLUMN "HeadingColor" TO heading_color; +ALTER TABLE pt."ListPreference" RENAME COLUMN "LineColor" TO line_color; +ALTER TABLE pt."ListPreference" RENAME COLUMN "HeadingFontSize" TO heading_font_size; +ALTER TABLE pt."ListPreference" RENAME COLUMN "TextFontSize" TO text_font_size; +ALTER TABLE pt."ListPreference" RENAME COLUMN "RequestSort" TO request_sort; +ALTER TABLE pt."ListPreference" RENAME COLUMN "GroupPassword" TO group_password; +ALTER TABLE pt."ListPreference" RENAME COLUMN "DefaultEmailType" TO default_email_type; +ALTER TABLE pt."ListPreference" RENAME COLUMN "IsPublic" TO is_public; +ALTER TABLE pt."ListPreference" RENAME COLUMN "TimeZoneId" TO time_zone_id; +ALTER TABLE pt."ListPreference" RENAME COLUMN "PageSize" TO page_size; +ALTER TABLE pt."ListPreference" RENAME COLUMN "AsOfDateDisplay" TO as_of_date_display; +ALTER TABLE pt."ListPreference" RENAME CONSTRAINT "PK_ListPreference" TO pk_list_preference; +ALTER TABLE pt."ListPreference" RENAME CONSTRAINT "FK_ListPreference_SmallGroup_SmallGroupId" TO fk_list_preference_small_group_id; +ALTER TABLE pt."ListPreference" RENAME CONSTRAINT "FK_ListPreference_TimeZone_TimeZoneId" TO fk_list_preference_time_zone_id; +ALTER TABLE pt."ListPreference" RENAME TO list_preference; + +ALTER INDEX pt."IX_ListPreference_TimeZoneId" RENAME TO ix_list_preference_time_zone_id; + +-- Small Group Member +ALTER TABLE pt."Member" RENAME COLUMN "MemberId" TO id; +ALTER TABLE pt."Member" RENAME COLUMN "SmallGroupId" TO small_group_id; +ALTER TABLE pt."Member" RENAME COLUMN "MemberName" TO member_name; +ALTER TABLE pt."Member" RENAME COLUMN "Email" TO email; +ALTER TABLE pt."Member" RENAME COLUMN "Format" TO email_format; +ALTER TABLE pt."Member" RENAME CONSTRAINT "PK_Member" TO pk_member; +ALTER TABLE pt."Member" RENAME CONSTRAINT "FK_Member_SmallGroup_SmallGroupId" TO fk_member_small_group_id; +ALTER TABLE pt."Member" RENAME TO member; + +ALTER INDEX pt."IX_Member_SmallGroupId" RENAME TO ix_member_small_group_id; + +-- Prayer Request +ALTER TABLE pt."PrayerRequest" RENAME COLUMN "PrayerRequestId" TO id; +ALTER TABLE pt."PrayerRequest" RENAME COLUMN "RequestType" TO request_type; +ALTER TABLE pt."PrayerRequest" RENAME COLUMN "UserId" TO user_id; +ALTER TABLE pt."PrayerRequest" RENAME COLUMN "SmallGroupId" TO small_group_id; +ALTER TABLE pt."PrayerRequest" RENAME COLUMN "EnteredDate" TO entered_date; +ALTER TABLE pt."PrayerRequest" RENAME COLUMN "UpdatedDate" TO updated_date; +ALTER TABLE pt."PrayerRequest" RENAME COLUMN "Requestor" TO requestor; +ALTER TABLE pt."PrayerRequest" RENAME COLUMN "Text" TO request_text; +ALTER TABLE pt."PrayerRequest" RENAME COLUMN "NotifyChaplain" TO notify_chaplain; +ALTER TABLE pt."PrayerRequest" RENAME COLUMN "Expiration" TO expiration; +ALTER TABLE pt."PrayerRequest" RENAME CONSTRAINT "PK_PrayerRequest" TO pk_prayer_request; +ALTER TABLE pt."PrayerRequest" RENAME CONSTRAINT "FK_PrayerRequest_User_UserId" TO fk_prayer_request_user_id; +ALTER TABLE pt."PrayerRequest" RENAME CONSTRAINT "FK_PrayerRequest_SmallGroup_SmallGroupId" TO fk_prayer_request_small_group_id; +ALTER TABLE pt."PrayerRequest" RENAME TO prayer_request; + +ALTER INDEX pt."IX_PrayerRequest_UserId" RENAME TO ix_prayer_request_user_id; +ALTER INDEX pt."IX_PrayerRequest_SmallGroupId" RENAME TO ix_prayer_request_small_group_id; +ALTER INDEX pt."IX_PrayerRequest_Requestor_TRGM" RENAME TO ix_prayer_request_trgm_requestor; +ALTER INDEX pt."IX_PrayerRequest_Text_TRGM" RENAME TO ix_prayer_request_trgm_request_text; +-- Small Group +ALTER TABLE pt."SmallGroup" RENAME COLUMN "SmallGroupId" TO id; +ALTER TABLE pt."SmallGroup" RENAME COLUMN "ChurchId" TO church_id; +ALTER TABLE pt."SmallGroup" RENAME COLUMN "Name" TO group_name; +ALTER TABLE pt."SmallGroup" RENAME CONSTRAINT "PK_SmallGroup" TO pk_small_group; +ALTER TABLE pt."SmallGroup" RENAME CONSTRAINT "FK_SmallGroup_Church_ChurchId" TO fk_small_group_church_id; +ALTER TABLE pt."SmallGroup" RENAME TO small_group; + +ALTER INDEX pt."IX_SmallGroup_ChurchId" RENAME TO ix_small_group_church_id; + +-- Time Zone +ALTER TABLE pt."TimeZone" RENAME COLUMN "TimeZoneId" TO id; +ALTER TABLE pt."TimeZone" RENAME COLUMN "Description" TO description; +ALTER TABLE pt."TimeZone" RENAME COLUMN "SortOrder" TO sort_order; +ALTER TABLE pt."TimeZone" RENAME COLUMN "IsActive" TO is_active; +ALTER TABLE pt."TimeZone" RENAME CONSTRAINT "PK_TimeZone" TO pk_time_zone; +ALTER TABLE pt."TimeZone" RENAME TO time_zone; + +-- User +ALTER TABLE pt."User" RENAME COLUMN "UserId" TO id; +ALTER TABLE pt."User" RENAME COLUMN "FirstName" TO first_name; +ALTER TABLE pt."User" RENAME COLUMN "LastName" TO last_name; +ALTER TABLE pt."User" RENAME COLUMN "EmailAddress" TO email; +ALTER TABLE pt."User" RENAME COLUMN "IsSystemAdmin" TO is_admin; +ALTER TABLE pt."User" RENAME COLUMN "PasswordHash" TO password_hash; +ALTER TABLE pt."User" RENAME COLUMN "Salt" TO salt; +ALTER TABLE pt."User" RENAME CONSTRAINT "PK_User" TO pk_pt_user; +ALTER TABLE pt."User" RENAME TO pt_user; + +-- User / Small Group +ALTER TABLE pt."User_SmallGroup" RENAME COLUMN "UserId" TO user_id; +ALTER TABLE pt."User_SmallGroup" RENAME COLUMN "SmallGroupId" TO small_group_id; +ALTER TABLE pt."User_SmallGroup" RENAME CONSTRAINT "PK_User_SmallGroup" TO pk_user_small_group; +ALTER TABLE pt."User_SmallGroup" RENAME CONSTRAINT "FK_User_SmallGroup_User_UserId" TO fk_user_small_group_user_id; +ALTER TABLE pt."User_SmallGroup" RENAME CONSTRAINT "FK_User_SmallGroup_SmallGroup_SmallGroupId" TO fk_user_small_group_small_group_id; +ALTER TABLE pt."User_SmallGroup" RENAME TO user_small_group; + +ALTER INDEX pt."IX_User_SmallGroup_SmallGroupId" RENAME TO ix_user_small_group_small_group_id;