Move redirect/tag map templates to GVE

- Fix chapter tests
- Apply generator string change for next release
This commit is contained in:
2024-03-12 18:58:15 -04:00
parent 90e6f78248
commit 5f114c7955
13 changed files with 309 additions and 317 deletions

View File

@@ -1,6 +1,10 @@
module MyWebLog.Views.Admin
open Giraffe.Htmx.Common
open Giraffe.ViewEngine
open Giraffe.ViewEngine.Accessibility
open Giraffe.ViewEngine.Htmx
open MyWebLog
open MyWebLog.ViewModels
/// The main dashboard
@@ -75,3 +79,217 @@ let dashboard (model: DashboardModel) app = [
]
]
]
/// Redirect Rule edit form
let redirectEdit (model: EditRedirectRuleModel) app = [
let url = relUrl app $"admin/settings/redirect-rules/{model.RuleId}"
h3 [] [ raw (if model.RuleId < 0 then "Add" else "Edit"); raw " Redirect Rule" ]
form [ _action url; _hxPost url; _hxTarget "body"; _method "post"; _class "container" ] [
antiCsrf app
input [ _type "hidden"; _name "RuleId"; _value (string model.RuleId) ]
div [ _class "row" ] [
div [ _class "col-12 col-lg-5 mb-3" ] [
div [ _class "form-floating" ] [
input [ _type "text"; _name "From"; _id "from"; _class "form-control"
_placeholder "From local URL/pattern"; _autofocus; _required; _value model.From ]
label [ _for "from" ] [ raw "From" ]
]
]
div [ _class "col-12 col-lg-5 mb-3" ] [
div [ _class "form-floating" ] [
input [ _type "text"; _name "To"; _id "to"; _class "form-control"; _placeholder "To URL/pattern"
_required; _value model.To ]
label [ _for "to" ] [ raw "To" ]
]
]
div [ _class "col-12 col-lg-2 mb-3" ] [
div [ _class "form-check form-switch" ] [
input [ _type "checkbox"; _name "IsRegex"; _id "isRegex"; _class "form-check-input"; _value "true"
if model.IsRegex then _checked ]
label [ _for "isRegex" ] [ raw "Use RegEx" ]
]
]
]
if model.RuleId < 0 then
div [ _class "row mb-3" ] [
div [ _class "col-12 text-center" ] [
label [ _class "me-1" ] [ raw "Add Rule" ]
div [ _class "btn-group btn-group-sm"; _roleGroup; _ariaLabel "New rule placement button group" ] [
input [ _type "radio"; _name "InsertAtTop"; _id "at_top"; _class "btn-check"; _value "true" ]
label [ _class "btn btn-sm btn-outline-secondary"; _for "at_top" ] [ raw "Top" ]
input [ _type "radio"; _name "InsertAtTop"; _id "at_bot"; _class "btn-check"; _value "false"
_checked ]
label [ _class "btn btn-sm btn-outline-secondary"; _for "at_bot" ] [ raw "Bottom" ]
]
]
]
div [ _class "row mb-3" ] [
div [ _class "col text-center" ] [
button [ _type "submit"; _class "btn btn-sm btn-primary" ] [ raw "Save Changes" ]
a [ _href (relUrl app "admin/settings/redirect-rules"); _class "btn btn-sm btn-secondary ms-3" ] [
raw "Cancel"
]
]
]
]
]
/// The list of current redirect rules
let redirectList (model: RedirectRule list) app = [
// Generate the detail for a redirect rule
let ruleDetail idx (rule: RedirectRule) =
let ruleId = $"rule_{idx}"
div [ _class "row mwl-table-detail"; _id ruleId ] [
div [ _class "col-5 no-wrap" ] [
txt rule.From; br []
small [] [
let ruleUrl = relUrl app $"admin/settings/redirect-rules/{idx}"
a [ _href ruleUrl; _hxTarget $"#{ruleId}"; _hxSwap $"{HxSwap.InnerHtml} show:#{ruleId}:top" ] [
raw "Edit"
]
if idx > 0 then
span [ _class "text-muted" ] [ raw " &bull; " ]
a [ _href $"{ruleUrl}/up"; _hxPost $"{ruleUrl}/up" ] [ raw "Move Up" ]
if idx <> model.Length - 1 then
span [ _class "text-muted" ] [ raw " &bull; " ]
a [ _href $"{ruleUrl}/down"; _hxPost $"{ruleUrl}/down" ] [ raw "Move Down" ]
span [ _class "text-muted" ] [ raw " &bull; " ]
a [ _class "text-danger"; _href ruleUrl; _hxDelete ruleUrl
_hxConfirm "Are you sure you want to delete this redirect rule?" ] [
raw "Delete"
]
]
]
div [ _class "col-5" ] [ txt rule.To ]
div [ _class "col-2 text-center" ] [ yesOrNo rule.IsRegex ]
]
h2 [ _class "my-3" ] [ raw app.PageTitle ]
article [] [
p [ _class "mb-3" ] [
a [ _href (relUrl app "admin/settings") ] [ raw "&laquo; Back to Settings" ]
]
div [ _class "container" ] [
div [ _class "row" ] [
div [ _class "col" ] [
a [ _href (relUrl app "admin/settings/redirect-rules/-1"); _class "btn btn-primary btn-sm mb-3"
_hxTarget "#rule_new" ] [
raw "Add Redirect Rule"
]
]
]
div [ _class "row" ] [
div [ _class "col" ] [
if List.isEmpty model then
div [ _id "rule_new" ] [
p [ _class "text-muted text-center fst-italic" ] [
raw "This web log has no redirect rules defined"
]
]
else
div [ _class "container g-0" ] [
div [ _class "row mwl-table-heading" ] [
div [ _class "col-5" ] [ raw "From" ]
div [ _class "col-5" ] [ raw "To" ]
div [ _class "col-2 text-center" ] [ raw "RegEx?" ]
]
]
div [ _class "row mwl-table-detail"; _id "rule_new" ] []
form [ _method "post"; _class "container g-0"; _hxTarget "body" ] [
antiCsrf app; yield! List.mapi ruleDetail model
]
]
]
]
p [ _class "mt-3 text-muted fst-italic text-center" ] [
raw "This is an advanced feature; please "
a [ _href "https://bitbadger.solutions/open-source/myweblog/advanced.html#redirect-rules"
_target "_blank" ] [
raw "read and understand the documentation on this feature"
]
raw " before adding rules."
]
]
]
/// Edit a tag mapping
let tagMapEdit (model: EditTagMapModel) app = [
h5 [ _class "my-3" ] [ txt app.PageTitle ]
form [ _hxPost (relUrl app "admin/settings/tag-mapping/save"); _method "post"; _class "container"
_hxTarget "#tagList"; _hxSwap $"{HxSwap.OuterHtml} show:window:top" ] [
antiCsrf app
input [ _type "hidden"; _name "Id"; _value model.Id ]
div [ _class "row mb-3" ] [
div [ _class "col-6 col-lg-4 offset-lg-2" ] [
div [ _class "form-floating" ] [
input [ _type "text"; _name "Tag"; _id "tag"; _class "form-control"; _placeholder "Tag"; _autofocus
_required; _value model.Tag ]
label [ _for "tag" ] [ raw "Tag" ]
]
]
div [ _class "col-6 col-lg-4" ] [
div [ _class "form-floating" ] [
input [ _type "text"; _name "UrlValue"; _id "urlValue"; _class "form-control"
_placeholder "URL Value"; _required; _value model.UrlValue ]
label [ _for "urlValue" ] [ raw "URL Value" ]
]
]
]
div [ _class "row mb-3" ] [
div [ _class "col text-center" ] [
button [ _type "submit"; _class "btn btn-sm btn-primary" ] [ raw "Save Changes" ]; raw " &nbsp; "
a [ _href (relUrl app "admin/settings/tag-mappings"); _class "btn btn-sm btn-secondary ms-3" ] [
raw "Cancel"
]
]
]
]
]
/// Display a list of the web log's current tag mappings
let tagMapList (model: TagMap list) app =
let tagMapDetail (map: TagMap) =
let url = relUrl app $"admin/settings/tag-mapping/{map.Id}"
div [ _class "row mwl-table-detail"; _id $"tag_{map.Id}" ] [
div [ _class "col no-wrap" ] [
txt map.Tag; br []
small [] [
a [ _href $"{url}/edit"; _hxTarget $"#tag_{map.Id}"
_hxSwap $"{HxSwap.InnerHtml} show:#tag_{map.Id}:top" ] [
raw "Edit"
]
span [ _class "text-muted" ] [ raw " &bull; " ]
a [ _href url; _hxDelete url; _class "text-danger"
_hxConfirm $"Are you sure you want to delete the mapping for “{map.Tag}”? This action cannot be undone." ] [
raw "Delete"
]
]
]
div [ _class "col" ] [ txt map.UrlValue ]
]
div [ _id "tagList"; _class "container" ] [
div [ _class "row" ] [
div [ _class "col" ] [
if List.isEmpty model then
div [ _id "tag_new" ] [
p [ _class "text-muted text-center fst-italic" ] [ raw "This web log has no tag mappings" ]
]
else
div [ _class "container g-0" ] [
div [ _class "row mwl-table-heading" ] [
div [ _class "col" ] [ raw "Tag" ]
div [ _class "col" ] [ raw "URL Value" ]
]
]
form [ _method "post"; _class "container g-0"; _hxTarget "#tagList"; _hxSwap HxSwap.OuterHtml ] [
antiCsrf app
div [ _class "row mwl-table-detail"; _id "tag_new" ] []
yield! List.map tagMapDetail model
]
]
]
]
|> List.singleton

View File

@@ -98,6 +98,10 @@ let shortTime app (instant: Instant) =
|> Option.defaultValue "--"
|> txt
/// Display "Yes" or "No" based on the state of a boolean value
let yesOrNo value =
raw (if value then "Yes" else "No")
/// Functions for generating content in varying layouts
module Layout =

View File

@@ -1,5 +1,6 @@
module MyWebLog.Views.Post
open Giraffe.Htmx.Common
open Giraffe.ViewEngine
open Giraffe.ViewEngine.Htmx
open MyWebLog
@@ -147,7 +148,7 @@ let chapterEdit (model: EditChapterModel) app = [
/// Display a list of chapters
let chapterList withNew (model: ManageChaptersModel) app =
form [ _method "post"; _id "chapter_list"; _class "container mb-3"; _hxTarget "this"; _hxSwap "outerHTML" ] [
form [ _method "post"; _id "chapter_list"; _class "container mb-3"; _hxTarget "this"; _hxSwap HxSwap.OuterHtml ] [
antiCsrf app
input [ _type "hidden"; _name "Id"; _value model.Id ]
div [ _class "row mwl-table-heading" ] [
@@ -170,7 +171,7 @@ let chapterList withNew (model: ManageChaptersModel) app =
else
let chapterUrl = relUrl app $"admin/post/{model.Id}/chapter/{idx}"
a [ _href chapterUrl; _hxGet chapterUrl; _hxTarget $"#chapter{idx}"
_hxSwap $"innerHTML show:#chapter{idx}:top" ] [
_hxSwap $"{HxSwap.InnerHtml} show:#chapter{idx}:top" ] [
raw "Edit"
]
span [ _class "text-muted" ] [ raw " &bull; " ]
@@ -179,12 +180,8 @@ let chapterList withNew (model: ManageChaptersModel) app =
]
]
]
div [ _class "col-3 col-md-2 col-lg-1 text-center" ] [
raw (match chapter.ImageUrl with Some _ -> "Y" | None -> "N")
]
div [ _class "col-3 col-md-2 col-lg-1 text-center" ] [
raw (match chapter.Location with Some _ -> "Y" | None -> "N")
]
div [ _class "col-3 col-md-2 col-lg-1 text-center" ] [ yesOrNo (Option.isSome chapter.ImageUrl) ]
div [ _class "col-3 col-md-2 col-lg-1 text-center" ] [ yesOrNo (Option.isSome chapter.Location) ]
])
div [ _class "row pb-3"; _id "chapter-1" ] [
let newLink = relUrl app $"admin/post/{model.Id}/chapter/-1"

View File

@@ -1,5 +1,6 @@
module MyWebLog.Views.User
open Giraffe.Htmx.Common
open Giraffe.ViewEngine
open Giraffe.ViewEngine.Htmx
open MyWebLog
@@ -12,7 +13,7 @@ let edit (model: EditUserModel) app =
div [ _class "col-12" ] [
h5 [ _class "my-3" ] [ txt app.PageTitle ]
form [ _hxPost (relUrl app "admin/settings/user/save"); _method "post"; _class "container"
_hxTarget "#userList"; _hxSwap "outerHTML show:window:top" ] [
_hxTarget "#userList"; _hxSwap $"{HxSwap.OuterHtml} show:window:top" ] [
antiCsrf app
input [ _type "hidden"; _name "Id"; _value model.Id ]
div [ _class "row" ] [
@@ -167,7 +168,8 @@ let userList (model: WebLogUser list) app =
div [ _class "container g-0" ] [
div [ _class "row mwl-table-detail"; _id "user_new" ] []
]
form [ _method "post"; _class "container g-0"; _hxTarget "this"; _hxSwap "outerHTML show:window:top" ] [
form [ _method "post"; _class "container g-0"; _hxTarget "this"
_hxSwap $"{HxSwap.OuterHtml} show:window:top" ] [
antiCsrf app
for user in model do
div [ _class "row mwl-table-detail"; _id $"user_{user.Id}" ] [
@@ -183,7 +185,7 @@ let userList (model: WebLogUser list) app =
let userUrl = relUrl app $"admin/settings/user/{user.Id}"
small [] [
a [ _href $"{userUrl}/edit"; _hxTarget $"#user_{user.Id}"
_hxSwap $"innerHTML show:#user_{user.Id}:top" ] [
_hxSwap $"{HxSwap.InnerHtml} show:#user_{user.Id}:top" ] [
raw "Edit"
]
if app.UserId.Value <> user.Id then