Introducing Tuxboard

Today, I introduce Tuxboard, which is an open-source dashboard library for ASP.NET 5 and higher and provide a quick-start

Written by Jonathan "JD" Danylko • Last Updated: • Tuxboard •

An exotic car's dashboard

What's Next?

One mantra developers should repeat often to themselves is DRY (Don't Repeat Yourself). Kind of ironic, isn't it?

When you start writing code, you begin writing similar code to what you did two years earlier. Now, you look at the code, refactor it, and continue to evolve it into something better.

After writing four dashboards throughout my career, it got to the point where I was taking legacy code and writing a new dashboard-esque module for some kind of new project.

This is where the story of Tuxboard picks up.

I turned the dashboard library into an open-source project.

Overview

Tuxboard is an open-source ASP.NET Core dashboard library where you can create any type of custom dashboard for ASP.NET Core web applications.

It is meant to be as compact and flexible as possible.

When the project initially started, I wanted to keep three goals in mind moving forward:

  1. Easy Integration - Simple enhancement to applications
  2. Declarative first, Programmatic second - The concept of modifying Tuxboard using HTML, CSS, and JavaScript/TypeScript was more enticing than modifying code, then compiling. However, the option of writing code is available.
  3. Extensibility - Providing simple hooks at the programmatic and database level to speed up custom development requirements.

The idea of building on-top-of instead of modifying-base-code was also the intent. 

Features of Tuxboard

For ASP.NET 5 or higher

There is even a version for .NET 7 (UPDATE: I'm currently working on the .NET 8.0 version of Tuxboard with newer examples. Stay tuned).

Available through NuGet

Using Azure DevOps, provided a fast way to compile and publish the library to NuGet.

Optimized Data-Access Using Entity Framework

Tuxboard has the ability to immediately create the schema and data for a sample database on installation using Migrations.

Custom Widgets

Tuxboard gives you the ability to create any number of custom widgets by adding a simple ViewComponent and a table entry to the Widgets table.

User-Specific Dashboards and Widgets

While you can create a simple static dashboard for everyone, user-specific dashboards provide a more customized look-and-feel for your users.

If you need a user-specific dashboard, there is an option to present only a list of widgets based on a user's role in the system (Role-based widgets). For example, if someone logs in and they're an administrator, they will see administrator-level widgets.

Along with specific widgets for users, I wanted to have the ability to have specific dashboards for role-based users as well. When someone logs into your system, users will be presented with a dashboard. Well, what kind of dashboard will they see as their default?

After everything is setup and a new user requests a dashboard, Tuxboard will create default dashboards with pre-filled widgets based on a user's role to kickstart the customization of their own dashboard.

CSS-Neutral

Since this is strictly a server-side library in C# and I'm not an expert in any of the modern front-end frameworks, you can create any type of HTML/JS Framework you choose.

TypeScript/Vanilla JS and Bootstrap will be used for the demos. If you would like to use a different CSS library (like Tailwind), I'll show you the hooks on how to update it in a future post.

Quick Start

Tuxboard's pre-requisites include:

  • .NET [Core] 5 or higher
  • Visual Studio 2019/2022
  • SQL Server (2016 or higher) (SSMS for updating records)

For starters, let's create a static dashboard in a brand new project.

1. Create a new ASP.NET [Core] Web Application

For this demo, I've named the project, "MyProject."

Screenshot of New ASP.NET Core 6.0 Project

2. Add the following NuGet packages to your project

  • Tuxboard.Core
  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools

3. Add a TuxConfig to your app.settings

Notes:

  • The WidgetFolder, ViewFolder, and ComponentFolder can be modified to your choosing. You can either create a new path for Tuxboard views/widgets/components or keep them in the same location. An example of this can be found in the UserDashboard example.
  • These paths are added onto the current list of default paths. Tuxboard does not clear the list of search paths.
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "TuxboardConfig": {
    "ConnectionString": "Data Source=localhost;Initial Catalog=MyProject;Integrated Security=True;MultipleActiveResultSets=True",
    "Schema": "dbo",
    "WidgetFolder": "/Pages/Shared/{0}.cshtml",
    "ViewFolder": "/Pages/{1}/{0}.cshtml",
    "ComponentFolder": "/Pages/Shared/{0}"
  }
}

4. Add the Tuxboard DbContext to your Program.cs

Notes:

  • Changes are in bold.

Program.cs

var builder = WebApplication.CreateBuilder(args);

// Tuxboard Configuration

var appConfig = new TuxboardConfig();
builder.Configuration
    .GetSection(nameof(TuxboardConfig))
    .Bind(appConfig);

// Tuxboard DbContext

builder.Services.AddDbContext<TuxDbContext>(options =>
{
    options.UseSqlServer(appConfig.ConnectionString,
        x => x.MigrationsAssembly("MyProject"));
});

// Add services to the container.

builder.Services.AddRazorPages();

// For Dependency Injection

builder.Services.AddTransient<IDashboardService, DashboardService>();
builder.Services.AddTransient<ITuxDbContext, TuxDbContext>();

var
 app = builder.Build();

// Configure the HTTP request pipeline.

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app
.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app
.UseAuthorization();

app
.MapRazorPages();

app
.Run();

5. Create the Database using Migrations

  1. Select the Package Manager Console in Visual Studio.
  2. Type: Add-Migration Initial. You should see a Migrations folder after creating your migration.
  3. To update the database, type: Update-Database (this will create the database for you based on the connection string in the appsettings.json). This will create default data for your dashboard.

6. Add the Dashboard to the Index Page

Next, we need to update our IndexModel page with the proper code and HTML.

index.cshtml.cs

public class IndexModel : PageModel
{
    private readonly ILogger<IndexModel> _logger;
    private readonly IDashboardService _service;
    private readonly TuxboardConfig _config;

   public Dashboard Dashboard { get; set; }

    public IndexModel(
        ILogger<IndexModel> logger,
        IDashboardService service,
        IConfiguration config)
    {
        _logger = logger;
        _service = service;
        config
            .GetSection(nameof(TuxboardConfig))
            .Bind(_config);
    }

   public async Task OnGet()
    {
        Dashboard = await _service.GetDashboardAsync(_config);
    }
}

index.cshtml(Paste this under the existing DIV on the page)

<section class="dashboard" id="@Model.Dashboard.DashboardId">
    @foreach (var tab in Model.Dashboard.Tabs)
    {
        foreach (var layout in tab.Layouts)
        {
            <div class="dashboard-tab">

               @foreach (var row in layout.LayoutRows.OrderBy(e => e.RowIndex))
                {
                    <div class="layout-row">

                       @foreach (var col in row.GetColumnLayout())
                        {
                            <div class="column @col.ColumnClass">

                               @foreach (var wp in row.WidgetPlacements.Where(y => y.ColumnIndex == col.Index).OrderBy(e => e.WidgetIndex))
                                {
                                    <!-- Widgets -->
                                    <div class="card @(wp.Collapsed ? "collapsed" : string.Empty)"
                                         @(Html.Raw(wp.Widget.Moveable ? "draggable=\"true\"" : ""))
                                         data-id="@wp.WidgetPlacementId">

                                       <div class="card-header">
                                            @wp.GetSettingValueByName("widgettitle")
                                        </div>

                                       <div class="card-body">
                                            @await Component.InvokeAsync(wp.Widget.Name, Model)
                                        </div>

                                   </div>
                                }
                            </div>
                        }
                        <div class="clearfix"></div>
                    </div>
                }
                <div class="clearfix"></div>
            </div>
        }
    }
</section>

7. Add Your Widgets

By default, there are three widgets in the SQL Server dbo.Widgets table: General Info, HelloWorld, and Sample Table.

Since we already have widgets created in the table when we performed an update-database, we now need to define each widget in the Components folder for the project.

For now, we'll just create the simplest widget: HelloWorld.

  • Under Pages/Shared, create a folder called Components
  • Create a HelloWorld folder under Components
  • In the HelloWorld folder, create two files:
    • Default.cshtml
    • HelloWorldViewComponent.cs

\Pages\Shared\Components\HelloWorld\Default.cshtml

@model Tuxboard.Core.Infrastructure.Models.WidgetModel

<
h4>Hello World Widget</h4>

\Pages\Shared\Components\HelloWorld\HelloWorldViewComponent.cshtml.cs

[ViewComponent(Name="helloworld")]
public class HelloWorldViewComponent : ViewComponent
{
    public IViewComponentResult Invoke(WidgetPlacement placement)
    {
        var widgetModel = new WidgetModel {Placement = placement};

       return View(widgetModel);
    }
}

You've just created your first widget for a dashboard.


8. Run the Project

When we run the project, you'll see the following screen.

Screenshot of Finished demo of a static dashboard

Congratulations! You have created a static dashboard for everyone who visits this page.

FAQ

Here's some questions I've received about this library. I'll try to answer as many questions as I can.

1. Why not include MS Identity?

There are two reasons why I didn't include it in the library.

  1. Not everyone uses Identity and may have rolled their own Authorization/Authentication scheme (which I wouldn't recommend). I've seen User Id's as integers and GUIDs so modifications can be made to accommodate specific requirements.
  2. Users may not want a user-centric dashboard; Simply display a static dashboard with no interactive widgets or settings.

I've decided to create one example (UserDashboard) using MS Identity. I built the example to use the commonly-seen "Register/Login" ASP.NET Demo Project. So you can register/login a user and they will be given a dashboard based on defaults ("you get a dashboard, You get a dashboard, and YOU get a dashboard!")

2. Why didn't you use <blah.blah> JavaScript framework?

I'll be honest. When I initially started this project, the dashboards were created using native JavaScript (ok, maybe a little bit of jQuery). I decided to move forward with native JS for five reasons:

  1. JavaScript is the common denominator for the web so it made sense to update my library. However, there is one exception I'm considering: TypeScript. At one point, I may move it over to TypeScript, but for now, I decided to keep it simple. I'm now using TypeScript for the examples.
  2. Native JavaScript is small when written well. With the web becoming bloated with all of these frameworks, I wanted to keep it lean.
  3. It uses most of the latest ES2016 features like fetch and arrow functions so extra libraries aren't required. (sorry IE).
  4. I found a Bootstrap library written in native JavaScript. ;-)
    Bootstrap 5 is coming and, by design, it uses vanilla JavaScript, no jQuery.

    Bootstrap 5 is already here! This is being used for the front-end demos in Tuxboard (specifically, only the Dropdown and Modal).
  5. With every single JS framework out there, I'm an expert in none of them. The resulting JavaScript should be small enough where a person could fork this repository and work on a Tuxboard.UI. version (i.e. Tuxboard.UI.Vue). Since there is JavaScript for only one page (the dashboard itself), I didn't feel there was a need to implement a huge JS framework for a dashboard.

3. Why Bootstrap?

Bootstrap has been around for a long time and has become a standard for developers for the ease of use and JavaScript library of UI components (Tailwind is coming up from the rear though and is becoming JUST as popular).

As I mentioned before, one of my goals for this dashboard library was to make Tuxboard declarative, then programmatic. This means if a change was required in the HTML, CSS, or JavaScript, it could be modified without compiling C# code. I wanted NO HTML, CSS, OR JAVASCRIPT IN C# CODE (already touched on that point before).

With that said, I wanted Tuxboard to be an easy way to swap out a CSS library and apply those styles to Widgets and Layouts based on the library and not rely on any C# code.

Most of the views are represented in each core ViewComponent and Widget layouts.

4. What's the difference between a Widget and a WidgetPlacement?

A Widget is a common component used on a dashboard and acts as merely a template for a dashboard.

A WidgetPlacement is a reference to a widget used on a dashboard with personalized settings attached to it. So essentially, it's a reference to a Widget. The WidgetPlacement is what appears on a dashboard where the user can move, add, delete, or adjust a certain widget.

Let's break this down with an example.

One example of a widget could be an RSS Feed Widget.

An RSS Feed widget would allow users to enter a favorite RSS URL feed and always see their updated news on the dashboard. Using that widget, a user can add multiple RSS Widgets to their dashboard. Every time they add a new RSS Widget, we are using the Widget template to create a WidgetPlacement on the dashboard. Each item on a dashboard is considered a WidgetPlacement referring to a Widget along with it's default WidgetSettings.

In programming terms, a Widget would be considered the abstract class with a WidgetPlacement as the instance of a Widget.

5. Do you have any sample projects?

Yes, I have three you can look at for inspiration or "how do things work?"

  • StaticDashboard - This is a simple static dashboard with no dynamic controls. We actually JUST created one above. ;-)
  • UserDashboard - Contains a public dashboard, but when a user logs in (using MS Identity), it displays a personalized dashboard; uses Razor Pages
  • UserSpecificDashboard - This is considered the "Kitchen sink" example. It contains the following:
    • Uses MVC Controllers
    • Logging in using MS Identity (from the default MS demo)
    • Rearranging layouts
    • Adding, moving, and deleting widgets
    • An example Tuxbar

(UPDATE: I'm currently working on the .NET 8.0 version of Tuxboard with newer examples. Stay tuned)

6. Why create a static dashboard?

Over the years, most users aren't looking for the flashy, glitzy features. They want something that works and can be expanded on later.

When I say "static dashboard," this translates to "public dashboard" which can't be modified and is set in stone. If you want a more personalized dashboard, you can add the user-specific features later.

Some companies like static dashboards with default widgets for everyone who visits. (shrug)

Where To Next?

With this first post, I hope this gives you a good understanding of how to start building your own dashboard.

I've used Tuxboard to create:

  • User-specific dashboards
    • Customized dashboard where they can add as many widgets as they want
    • Ability to move widgets around on the dashboard
    • (optional) Add a toolbar to each widget
    • (optional) Settings panel for each widget
  • A toolbar for the dashboard (TuxBar) for enhancements
  • A Layout Manager for multiple columns and various LayoutRows

I'll be writing about each of these topics in future posts since this is now in the general public.

Stay tuned for more on the Tuxboard page.

What's Next?

Did you like this content? Show your support by buying me a coffee.

Buy me a coffee  Buy me a coffee
Picture of Jonathan "JD" Danylko

Jonathan Danylko is a web architect and entrepreneur who's been programming for over 25 years. He's developed websites for small, medium, and Fortune 500 companies since 1996.

He currently works at Insight Enterprises as an Principal Software Engineer Architect.

When asked what he likes to do in his spare time, he replies, "I like to write and I like to code. I also like to write about code."

comments powered by Disqus