So faced with the same need (custom dashboard modifications) I went about working with the cryptic suggestions above. I managed to figure it out without too much trouble so thought I’d share it here.
First, it’s a little cumbersome as there’s some sort of cshtml conversion tool used to auto generate RazorPage partial classes. As a result, you have to write html within code.
So. First step, add some routes and a navigation menu item. I did this in my startup.cs just before calling app.UseHangfireDashboard
DashboardRoutes.Routes.AddRazorPage("/management", x=> new ManagementPage());
NavigationMenu.Items.Add(page => new MenuItem("Management", page.Url.To("/management"))
{
Active = page.RequestPath.StartsWith("/management")
});
This will add a new top bar navigation item and link it to /management url.
Now you need to create the ManagementPage defined above. It’s pretty basic right now, but shows you how to get started.
public class ManagementPage : RazorPage
{
public override void Execute()
{
WriteLiteral("\r\n");
Layout = new LayoutPage("Management");
WriteLiteral("<div class=\"row\">\r\n");
WriteLiteral("<div class=\"col-md-3\">\r\n");
Write(Html.RenderPartial(new CustomSidebarMenu(ManagementSidebarMenu.Items)));
WriteLiteral("</div>\r\n");
WriteLiteral("<div class=\"col-md-9\">\r\n");
WriteLiteral("<h1 class=\"page-header\">\r\n");
Write("Management");
WriteLiteral("</h1>\r\n");
WriteLiteral("<div class=\"alert alert-success\">\r\n");
Write("Nothing");
WriteLiteral("\r\n</div>\r\n");
WriteLiteral("\r\n</div>\r\n");
}
}
Next, you’ll notice I also played with adding a CustomSidebarMenu. This is an exact copy of the JobsSidebarMenu. However, since the classes end up being internal I couldn’t re-use them.
public class CustomSidebarMenu : RazorPage
{
public CustomSidebarMenu([NotNull] IEnumerable<Func<RazorPage, MenuItem>> items)
{
if (items == null) throw new ArgumentNullException(nameof(items));
Items = items;
}
public IEnumerable<Func<RazorPage, MenuItem>> Items { get; }
public override void Execute()
{
WriteLiteral("\r\n");
if (!Items.Any()) return;
WriteLiteral("<div id=\"stats\" class=\"list-group\">\r\n");
foreach (var item in Items)
{
var itemValue = item(this);
WriteLiteral("<a href=\"");
Write(itemValue.Url);
WriteLiteral("\" class=\"list-group-item ");
Write(itemValue.Active ? "active" : null);
WriteLiteral("\">\r\n");
Write(itemValue.Text);
WriteLiteral("\r\n<span class=\"pull-right\">\r\n");
foreach (var metric in itemValue.GetAllMetrics())
{
Write(Html.InlineMetric(metric));
}
WriteLiteral("</span>\r\n</a>\r\n");
}
WriteLiteral("</div>\r\n");
}
}
Now, here’s a sample of the custom sidebar definition.
public static class ManagementSidebarMenu
{
public static readonly List<Func<RazorPage, MenuItem>> Items
= new List<Func<RazorPage, MenuItem>>();
static ManagementSidebarMenu()
{
Items.Add(page => new MenuItem("Index", page.Url.To("/management/index"))
{
Active = page.RequestPath.StartsWith("/management/index")
});
Items.Add(page => new MenuItem("Import", page.Url.To("/management/import"))
{
Active = page.RequestPath.StartsWith("/management/import")
});
Items.Add(page => new MenuItem("Misc", page.Url.To("/management/misc"))
{
Active = page.RequestPath.StartsWith("/management/misc")
});
Items.Add(page => new MenuItem("Email", page.Url.To("/management/email"))
{
Active = page.RequestPath.StartsWith("/management/email")
});
}
}
That’s about as far as I have gotten so far, but it covers the basics of getting started. You should be able to add additional routes to then use the custom menu and more pages to do what you need.
I’ll add to this post if I find anything else useful.