Build Beautiful Admin UIs, Fast

Build Beautiful Admin UIs, Fast Background
9 min read

Built into the latest blazor-vue project template include examples of both Server Multi Razor Page and Client rendered Admin UI Pages demonstrating how to use the new <sidebar-layout> and <auto-query-grid> Vue Tailwind components to build beautiful Admin UI Pages in minutes.

As we use AutoQuery to develop most new APIs we often lean on Locode's instant Auto Admin UI at the start of every project to manage backend RDBMS tables freeing, us to focus most of our efforts on the user-facing features adding the most value.

This was also the case for CreatorKit where its Locode Admin UI:

Which was sufficient enough to develop CreatorKit's embeddable Tailwind Components, however the Admin Portal itself also makes up a significant part of the user-facing value proposition in CreatorKit that we ultimately decided to develop a Custom UI for its Admin Portal in order to gain complete control of its UX and freedom to create the optimized UI we want to in future.

Adopting a Simple, Modern JavaScript approach free of build tools complexity with the instant live reload of a simple static index.html yielded the most productive workflow, combined with all built-in UIs having been rebuilt in reusable Vue 3 components meant we were able to replicate most of Locode's functionality into a customizable Vue App in no time!

App screenshot

SidebarLayout Vue Component

So much so that we believe this is a great progressive option for others needing to move beyond Locode or looking to create their own customizable Admin UI. To minimize the effort we've encapsulated Tailwind's Responsive Sidebar Navigation into a reusable <SidebarLayout /> Vue 3 component and upgraded the Blazor Vue project template to include both a Razor Pages server rendered Admin UI:

As well as a client rendered Admin UI created from a single static index.html

We'll quickly go through how we can easily extend each layout to with new pages and AutoQueryGrid components to manage new tables.

Client Admin UI

The customizable UI is maintained within a single JS Object literal that defines all the sections in the Admin UI. Each section defines the Menu Item on the Sidebar, the Title of the Page and the Vue 3 component body to display when selected:

const sections = {
   Dashboard: {
      icon: `<svg fill="none">...</svg>`,
      component: {
         template:`
            <div>
              <dl class="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
                <div v-for="stat in stats" @click="$emit('nav',stat.label)" class="...">
                  <dt class="...">Total {‎{humanize(stat.label)}‎}</dt>
                  <dd class="...">{‎{formatNumber(stat.total)}‎}</dd>
                </div>
              </dl>
            </div>`,
         setup() {
            const client = useClient()
            const stats = ref([])
            client.swr(new AdminData(), r => stats.value = r.response?.pageStats || [])
            const formatNumber = value => new Intl.NumberFormat().format(value)
            return { stats, humanize, formatNumber }
         }
      },
   },
   Bookings: {
      type: 'Booking',
      component: {
         template:`<AutoQueryGrid :type="type" 
            selected-columns="id,name,roomType,roomNumber,bookingStartDate,cost,couponId,discount"
            :header-titles="{ roomNumber:'Room No', bookingStartDate:'Start Date', couponId:'Coupon' }"
            :visible-from="{ roomNumber:'lg', cost:'md', couponId:'xl', discount:'never' }" />`,
      },
   },
   Coupons: {
      type: 'Coupon',
      component: {
         template:`<AutoQueryGrid :type="type" />`,
      },
   },
} 

Each section can use optional properties to customize their appearance with the properties below:

  • icon - SVG Icon to use in the Sidebar Menu Item
  • type - Metadata Type to use to populate icon or reference in component
  • label - The label of the menu item on the Sidebar
  • title - The Title of the page
  • group - The Sidebar group where the menu item should be displayed in
  • component - The page's Vue 3 component body

All properties except for component are optional, defaulting to the section name and default icon when not provided.

The above example creates an Admin UI with 3 different types of pages:

  • A custom component to display the Dashboard
  • A customized responsive AutoQueryGrid to manage Bookings
  • A default AutoQueryGrid to manage Coupons

Most of the time you'll just need to use the default AutoQueryGrid to enable a CRUD UI to manage your RDBMS tables.

Which was all that was needed to render Blazor Diffusion's Admin UI:

const sections = {
     Dashboard: {
         icon: `<svg>...</svg>`,
         component: {
             template:`
             <div>
               <dl class="...">
                 <div v-for="stat in stats" @click="$emit('nav',stat.label)" class="...">
                   <dt class="...">Total {‎{humanize(stat.label)}‎}</dt>
                   <dd class="...">{‎{formatNumber(stat.total)}‎}</dd>
                 </div>
               </dl>
             </div>`,
             setup() {
                 const client = useClient()
                 const stats = ref([])
                 client.swr(new AdminData(), r => stats.value = r.response?.pageStats || [])
                 const formatNumber = value => new Intl.NumberFormat().format(value)
                 return { stats, humanize, formatNumber }
             }
         },
     },
     Creatives: {
         type: 'Creative',
         component: { template:`<AutoQueryGrid :type="type" />` },
     },
     Artists: {
         type: 'Artist',
         component: { template:`<AutoQueryGrid :type="type" />` },
     },
     Modifiers: {
         type: 'Modifier',
         component: { template:`<AutoQueryGrid :type="type" />` },
     },
     CreativeArtists: {
         type: 'CreativeArtist',
         component: { template:`<AutoQueryGrid :type="type" />` },
     },
     CreativeModifiers: {
         type: 'CreativeModifier',
         component: { template:`<AutoQueryGrid :type="type" />` },
     },
     Artifacts: {
         type: 'Artifact',
         component: { template:`<AutoQueryGrid :type="type" />` },
     },
     ArtifactLikes: {
         type: 'ArtifactLike',
         component: { template:`<AutoQueryGrid :type="type" />` },
     },
     ArtifactComments: {
         type: 'ArtifactComment',
         component: { template:`<AutoQueryGrid :type="type" />` },
     },
     ArtifactCommentVotes: {
         type: 'ArtifactCommentVote',
         component: { template:`<AutoQueryGrid :type="type" />` },
     },
     Albums: {
         group: 'Albums',
         type: 'Album',
         component: { template:`<AutoQueryGrid :type="type" />` },
     },
     AlbumArtifacts: {
         group: 'Albums',
         type: 'AlbumArtifact',
         component: { template:`<AutoQueryGrid :type="type" />` },
     },
     AlbumLikes: {
         group: 'Albums',
         type: 'AlbumLike',
         component: { template:`<AutoQueryGrid :type="type" />` },
     },
     ArtifactStats: {
         group: 'Analytics',
         type: 'ArtifactStat',
         component: { template:`<AutoQueryGrid :type="type" />` },
     },
     SearchStats: {
         group: 'Analytics',
         type: 'SearchStat',
         component: { template:`<AutoQueryGrid :type="type" />` },
     },
     Signups: {
         group: 'Analytics',
         type: 'Signup',
         component: { template:`<AutoQueryGrid :type="type" />` },
     },
 }

Server Admin UI

For developers preferring to develop Server rendered Web Apps the blazor project template also includes a Multi Page App (MPA) Razor Pages & Vue App which defines the Admin Sections in the custom C# Admin/

<NavList Title="Admin">
    <NavListItem Title="Bookings" href="/admin/bookings" Icon=@typeof(Booking).GetIcon()>
        Create and Manage Bookings
    </NavListItem>
    <NavListItem Title="Coupons" href="/admin/coupons" Icon=@typeof(Coupon).GetIcon()>
        Create and Manage Coupons
    </NavListItem>
</NavList>

Where each section links to the different Admin UI Razor Pages:

Admin/Index.razor

Generates the Admin UI Dashboard which retrieves the data for the page from the AdminData API invoked with the internal Service Gateway:

@page
@{
    ViewData["Title"] = "Dashboard";
    var adminData = await Html.Gateway().SendAsync(new AdminData());
}

<div>
    <dl class="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
        @foreach (var item in adminData.PageStats)
        {
            <a href="/admin/@item.Label.ToLower()" class="...">
                <dt class="...">Total @item.Label</dt>
                <dd class="...">@item.Total</dd>
            </a>
        }
    </dl>
</div>

Admin/Bookings.razor

Defines the same responsive AutoQueryGrid to manage the Bookings RDBMS Table:

<AutoQueryGrid Model="Booking" Apis="Apis.AutoQuery<QueryBookings,CreateBooking,UpdateBooking,DeleteBooking>()" />

Admin/Coupons.razor

Utilizes the default AutoQueryGrid component to manage the Coupons RDBMS Table:

<AutoQueryGrid Model="Coupon" Apis="Apis.AutoQuery<QueryCoupons>()" />

Creating Custom Admin UIs

Easiest way to start creating Custom Admin UIs is to start with a new blazor-vue Razor Pages template containing both client and server rendered Admin UIs:

Alternatively you can download their pages to incorporate them into your existing Tailwind Projects:

Client Admin UI

Server Admin UI

Conclusion

In summary, the <SidebarLayout> and <AutoQueryGrid> Tailwind Vue Components are valuable tools for software developers seeking to expedite creation of efficient and user-friendly Admin UIs by providing a solid foundation to effortlessly build beautiful, customizable Admin Pages in no time, freeing up valuable time & developer resources to focus the majority of efforts into creating the user-facing features that matter most.