Skip to content

Commit f2ca161

Browse files
authored
Merge pull request #142 from Geta/feature/edit-redirects
Add possibility to edit redirects
2 parents 467ccab + 90b50f1 commit f2ca161

File tree

12 files changed

+443
-230
lines changed

12 files changed

+443
-230
lines changed

src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Components/Pager/Default.cshtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
@model Geta.NotFoundHandler.Admin.Pages.Geta.NotFoundHandler.Admin.Components.Pager.PagerViewModel
22

33
<nav aria-label="Table pagination">
4-
<ul class="pagination">
4+
<ul class="pagination pagination-spacing">
55
<li class="page-item @(Model.HasPreviousPage ? string.Empty : "disabled")">
66
<a class="page-link" href="@Model.PageUrl(Model.PageNumber-1)" aria-label="Previous">
77
<span aria-hidden="true">&laquo;</span>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Microsoft.AspNetCore.Mvc.ModelBinding;
2+
3+
namespace Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Extensions;
4+
5+
public static class ModelStateExtensions
6+
{
7+
public static ModelStateDictionary RemoveNestedKeys(this ModelStateDictionary source, string prefix)
8+
{
9+
foreach (var key in source.Keys)
10+
{
11+
if (key.StartsWith($"{prefix}."))
12+
{
13+
source.Remove(key);
14+
}
15+
}
16+
17+
return source;
18+
}
19+
}

src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Index.cshtml

Lines changed: 169 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88

99
@await Component.InvokeAsync("Card", new { message = Model.Message })
1010

11+
<!-- Search form -->
1112
<form method="get">
1213
<div class="search-container input-group flex-nowrap">
13-
<input class="form-control w-100 border-end-0" type="text" placeholder="Search" aria-label="Search" id="search" name="q" value="@Model.Query">
14+
<input class="form-control w-100 border-end-0" type="text" placeholder="Search" aria-label="Search" id="search"name="q" value="@Model.Query">
1415
<span class="input-group-append">
1516
<button class="btn btn-outline-secondary border-start-0 border" type="button" data-clear="#search">
1617
<span data-feather="x"></span>
@@ -22,34 +23,35 @@
2223
</div>
2324
</form>
2425

25-
<form method="post">
26-
<div class="table-responsive mt-3">
27-
<table class="table table-hover table-sm" aria-label="Redirects">
28-
<thead>
29-
<tr>
30-
<th>
31-
<vc:sortable-header-cell key="@nameof(CustomRedirect.NewUrl)" display-name="New URL"/>
32-
</th>
33-
<th>
34-
<vc:sortable-header-cell key="@nameof(CustomRedirect.OldUrl)" display-name="Old URL"/>
35-
</th>
36-
<th class="col-1 text-center">
37-
<vc:sortable-header-cell key="@nameof(CustomRedirect.WildCardSkipAppend)" display-name="Wildcard"/>
38-
</th>
39-
<th class="col-1">
40-
<vc:sortable-header-cell key="@nameof(CustomRedirect.RedirectType)" display-name="Redirect Type"/>
41-
</th>
42-
<th class="col-1"></th>
43-
</tr>
44-
</thead>
45-
<tbody>
46-
<tr>
26+
<!-- Redirect table -->
27+
<div class="table-responsive mt-3">
28+
<table class="table table-hover table-sm" aria-label="Redirects">
29+
<thead>
30+
<tr>
31+
<th>
32+
<vc:sortable-header-cell key="@nameof(CustomRedirect.OldUrl)" display-name="Old URL"/>
33+
</th>
34+
<th>
35+
<vc:sortable-header-cell key="@nameof(CustomRedirect.NewUrl)" display-name="New URL"/>
36+
</th>
37+
<th class="col-1 text-center">
38+
<vc:sortable-header-cell key="@nameof(CustomRedirect.WildCardSkipAppend)" display-name="Wildcard"/>
39+
</th>
40+
<th class="col-1">
41+
<vc:sortable-header-cell key="@nameof(CustomRedirect.RedirectType)" display-name="Redirect Type"/>
42+
</th>
43+
<th class="col-1"></th>
44+
</tr>
45+
</thead>
46+
<tbody>
47+
<tr>
48+
<form method="post">
4749
<td>
48-
<input type="text" class="form-control" asp-for="CustomRedirect.OldUrl">
50+
<input required type="text" class="form-control" asp-for="CustomRedirect.OldUrl">
4951
<span asp-validation-for="CustomRedirect.OldUrl" class="text-danger"></span>
5052
</td>
5153
<td>
52-
<input type="text" class="form-control" asp-for="CustomRedirect.NewUrl">
54+
<input required type="text" class="form-control" asp-for="CustomRedirect.NewUrl">
5355
<span asp-validation-for="CustomRedirect.NewUrl" class="text-danger"></span>
5456
</td>
5557
<td class="text-center align-middle">
@@ -58,45 +60,157 @@
5860
<td>
5961
<select class="form-select" asp-for="CustomRedirect.RedirectType">
6062
<option value="@RedirectType.Temporary"
61-
selected="@(Options.Value.DefaultRedirectType == RedirectType.Temporary)">
62-
@RedirectType.Temporary
63-
</option>
63+
selected="@(Options.Value.DefaultRedirectType == RedirectType.Temporary)">@RedirectType.Temporary</option>
6464
<option value="@RedirectType.Permanent"
6565
selected="@(Options.Value.DefaultRedirectType == RedirectType.Permanent)">
66-
@RedirectType.Permanent
67-
</option>
66+
@RedirectType.Permanent</option>
6867
</select>
6968
</td>
7069
<td>
7170
<div class="d-grid gap-2">
72-
<button type="submit" class="btn btn-primary"
73-
asp-page-handler="create">
71+
<button type="submit" class="btn btn-primary" asp-page-handler="create">
7472
<span data-feather="plus"></span> add
7573
</button>
7674
</div>
7775
</td>
78-
</tr>
79-
@foreach (var item in Model.Items)
80-
{
81-
<tr class="align-middle">
82-
<td>@item.OldUrl</td>
83-
<td>@item.NewUrl</td>
84-
<td class="text-center align-middle">
85-
@await Component.InvokeAsync("CheckboxReadonly", new { isChecked = item.WildCardSkipAppend })
86-
</td>
87-
<td>@item.RedirectType</td>
88-
<td>
89-
<div class="d-grid gap-2">
90-
<button type="submit" class="btn btn-danger"
91-
asp-page-handler="delete" asp-route-oldurl="@item.OldUrl">
76+
</form>
77+
</tr>
78+
79+
@for (int i = 0; i < Model.Items.Count; i++)
80+
{
81+
var item = Model.Items[i];
82+
<tr class="align-middle">
83+
84+
<!-- Display Mode -->
85+
<td>@item.OldUrl</td>
86+
<td>@item.NewUrl</td>
87+
<td class="text-center align-middle">
88+
@await Component.InvokeAsync("CheckboxReadonly", new { isChecked = item.WildCardSkipAppend })
89+
</td>
90+
<td>@item.RedirectType</td>
91+
<td>
92+
@{
93+
var editModalId = $"editModal{i}";
94+
var modalTitleId = $"modalTitle{i}";
95+
}
96+
97+
<div class="modal fade" id="@editModalId" tabindex="-1" aria-labelledby="@modalTitleId">
98+
<div class="modal-dialog">
99+
<div class="modal-content edit-modal">
100+
<div class="modal-header">
101+
<h5 class="modal-title" id="@modalTitleId">Edit</h5>
102+
<button type="button" class="btn-close" data-bs-dismiss="modal"
103+
aria-label="Close"></button>
104+
</div>
105+
<div class="modal-body">
106+
<form method="post" id="form-@editModalId">
107+
<div class="input-group mb-2">
108+
<div class="input-group-prepend">
109+
<span class="input-group-text">Old Url</span>
110+
</div>
111+
<textarea required="required" class="form-control" name="@Html.NameFor(m => m.EditRedirect.OldUrl)" rows="1">@item.OldUrl</textarea>
112+
</div>
113+
114+
<div class="input-group mb-2">
115+
<div class="input-group-prepend">
116+
<span class="input-group-text">New Url</span>
117+
</div>
118+
<textarea required="required" class="form-control" name="@Html.NameFor(m => m.EditRedirect.NewUrl)" rows="1">@item.NewUrl</textarea>
119+
</div>
120+
121+
<div class="d-flex flex-row">
122+
<div class="flex-fill">
123+
<label>Wildcard</label>
124+
<div class="input-group mb-2 text-center align-middle">
125+
@if (item.WildCardSkipAppend)
126+
{
127+
<input type="checkbox" class="form-check-input"
128+
asp-for="EditRedirect.WildCardSkipAppend" value="true"
129+
checked="checked"/>
130+
}
131+
else
132+
{
133+
<input type="checkbox" class="form-check-input"
134+
asp-for="EditRedirect.WildCardSkipAppend"
135+
value="true"/>
136+
}
137+
</div>
138+
</div>
139+
140+
<div class="flex-fill">
141+
<label>RedirectType</label>
142+
<div class="input-group mb-2">
143+
<select class="form-select" asp-for="EditRedirect.RedirectType">
144+
@if (item.RedirectType == RedirectType.Temporary)
145+
{
146+
<option value="@RedirectType.Temporary"
147+
selected>@RedirectType.Temporary</option>
148+
}
149+
else
150+
{
151+
<option
152+
value="@RedirectType.Temporary">@RedirectType.Temporary</option>
153+
}
154+
155+
@if (item.RedirectType == RedirectType.Permanent)
156+
{
157+
<option value="@RedirectType.Permanent"
158+
selected>@RedirectType.Permanent</option>
159+
}
160+
else
161+
{
162+
<option
163+
value="@RedirectType.Permanent">@RedirectType.Permanent</option>
164+
}
165+
</select>
166+
</div>
167+
</div>
168+
</div>
169+
170+
171+
<input type="hidden" asp-for="EditRedirect.Id" value="@item.Id"/>
172+
173+
<button type="submit"
174+
class="mt-3 btn btn-primary"
175+
asp-route-pagenumber="@Model.Items.PageNumber"
176+
asp-route-query="@Model.Query"
177+
asp-page-handler="update">Update
178+
</button>
179+
</form>
180+
</div>
181+
</div>
182+
</div>
183+
</div>
184+
185+
<div class="d-grid gap-2">
186+
<button
187+
type="submit"
188+
class="btn btn-primary modal-trigger"
189+
data-bs-toggle="modal"
190+
data-bs-target="#@editModalId">
191+
<span data-feather="edit"></span> edit
192+
</button>
193+
</div>
194+
</td>
195+
<td>
196+
<div class="d-grid gap-2">
197+
<form method="post">
198+
<button
199+
type="submit"
200+
class="btn btn-danger"
201+
asp-page-handler="delete"
202+
asp-route-id="@item.Id"
203+
asp-route-pagenumber="@Model.Items.PageNumber"
204+
asp-route-query="@Model.Query">
92205
<span data-feather="trash-2"></span> delete
93206
</button>
94-
</div>
95-
</td>
96-
</tr>
97-
}
98-
</tbody>
99-
</table>
100-
@await Component.InvokeAsync(typeof(Geta.NotFoundHandler.Admin.Pages.Geta.NotFoundHandler.Admin.Components.Pager.PagerViewComponent), new { Model.Items })
101-
</div>
102-
</form>
207+
</form>
208+
</div>
209+
</td>
210+
</tr>
211+
}
212+
</tbody>
213+
</table>
214+
215+
@await Component.InvokeAsync(typeof(Geta.NotFoundHandler.Admin.Pages.Geta.NotFoundHandler.Admin.Components.Pager.PagerViewComponent), new { Model.Items })
216+
</div>

0 commit comments

Comments
 (0)