Skip to content

Commit f8135f7

Browse files
committed
refactor(ui): migrate from MudBlazor to Microsoft Fluent UI
Replaced the MudBlazor component library with Microsoft.FluentUI.AspNetCore.Components across the entire web application. This change aligns the project with a more modern and native Microsoft UI framework for Blazor. Key changes include: - Updated project dependencies to include Fluent UI and remove MudBlazor. - Refactored all Razor components (Index.razor, MainLayout.razor) to use Fluent UI components. - Updated Program.cs to register Fluent UI services. - Updated documentation (README, AGENTS.md, tech-stack.md) to reflect the new UI framework. - Updated CHANGELOG.md with the change.
1 parent 4ca5cf3 commit f8135f7

File tree

11 files changed

+196
-108
lines changed

11 files changed

+196
-108
lines changed
File renamed without changes.

.ai/tech-stack.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
* **Centralized Logic:** Enables the creation of a centralized service to read the `config.yaml`, run scans, and enforce the core policies (e.g., file presence, workflow permissions) described in section 3.4.
77
* **API Integration:** Serves as the backbone for all interactions with the GitHub API, handling the logic for scanning repositories and executing automated actions.
88

9-
### Frontend: Blazor Server + MudBlazor
9+
### Frontend: Blazor Server + Microsoft Fluent UI
1010

11-
* **Rapid UI Development:** The MudBlazor component library allows for the quick assembly of the compliance dashboard (US-006), including the repository list, filter, and summary metrics, accelerating MVP delivery.
11+
* **Rapid UI Development:** The Microsoft Fluent UI component library allows for the quick assembly of the compliance dashboard (US-006), including the repository list, filter, and summary metrics, accelerating MVP delivery.
1212
* **Simplified Real-time Updates:** Blazor Server's architecture provides real-time UI feedback for on-demand scans (US-008) out-of-the-box, removing the need for a separate, more complex real-time messaging service.
1313
* **Interactive Experience:** Facilitates the creation of a responsive user experience, including the real-time repository filter (US-007) and the guided setup for first-time users (US-003).
1414

10xGitHubPolicies.App/10xGitHubPolicies.App.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
2121
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.12" />
2222
<PackageReference Include="Hangfire.SqlServer" Version="1.8.12" />
23-
<PackageReference Include="MudBlazor" Version="8.13.0" />
23+
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components" Version="4.13.0" />
24+
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components.Icons" Version="4.13.0" />
2425
<PackageReference Include="Octokit" Version="12.0.0" />
2526
<PackageReference Include="Octokit.Extensions" Version="1.0.7" />
2627
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />

10xGitHubPolicies.App/Pages/Index.razor

Lines changed: 143 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -3,103 +3,168 @@
33
@using _10xGitHubPolicies.App.Services.Scanning
44
@using _10xGitHubPolicies.App.ViewModels
55
@using Hangfire
6+
@using Microsoft.FluentUI.AspNetCore.Components
7+
@using Microsoft.FluentUI.AspNetCore.Components.Icons
8+
@using System.Linq
69
@inject IDashboardService DashboardService
710
@inject IScanningService ScanningService
811
@inject IBackgroundJobClient BackgroundJobClient
912

1013
<PageTitle>Compliance Dashboard</PageTitle>
1114

12-
<MudGrid Justify="Justify.FlexStart" Spacing="3">
13-
<MudItem xs="12" sm="6" md="3">
14-
<MudPaper Elevation="2" Class="pa-4" Style="height: 100%;">
15-
<MudText Typo="Typo.h6">Overall Compliance</MudText>
16-
<MudText Typo="Typo.h3">@($"{_viewModel?.CompliancePercentage:F2}%")</MudText>
17-
</MudPaper>
18-
</MudItem>
19-
<MudItem xs="12" sm="6" md="3">
20-
<MudPaper Elevation="2" Class="pa-4" Style="height: 100%;">
21-
<MudText Typo="Typo.h6">Total Repositories</MudText>
22-
<MudText Typo="Typo.h3">@_viewModel?.TotalRepositories</MudText>
23-
</MudPaper>
24-
</MudItem>
25-
<MudItem xs="12" sm="6" md="3">
26-
<MudPaper Elevation="2" Class="pa-4" Style="height: 100%; background-color: var(--mud-palette-success); color: var(--mud-palette-success-text);">
27-
<MudText Typo="Typo.h6">Compliant</MudText>
28-
<MudText Typo="Typo.h3">@_viewModel?.CompliantRepositories</MudText>
29-
</MudPaper>
30-
</MudItem>
31-
<MudItem xs="12" sm="6" md="3">
32-
<MudPaper Elevation="2" Class="pa-4" Style="height: 100%; background-color: var(--mud-palette-error); color: var(--mud-palette-error-text);">
33-
<MudText Typo="Typo.h6">Non-Compliant</MudText>
34-
<MudText Typo="Typo.h3">@_viewModel?.NonCompliantRepositoriesCount</MudText>
35-
</MudPaper>
36-
</MudItem>
37-
</MudGrid>
38-
39-
<MudPaper Elevation="2" Class="mt-4">
40-
<MudTable Items="@_viewModel?.NonCompliantRepositories" Dense="true" Hover="true" Filter="new Func<NonCompliantRepositoryViewModel, bool>(FilterFunc)">
41-
<ToolBarContent>
42-
<MudTextField @bind-Value="_nameFilter" Placeholder="Filter by name..." Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField>
43-
<MudSpacer />
44-
<MudButton Variant="Variant.Filled"
45-
Color="Color.Primary"
46-
StartIcon="@Icons.Material.Filled.Refresh"
47-
OnClick="StartScan"
48-
Disabled="_isScanning">
49-
@if (_isScanning)
50-
{
51-
<MudProgressCircular Class="ms-n1" Size="Size.Small" Indeterminate="true" />
52-
<MudText Class="ms-2">Scanning...</MudText>
53-
}
54-
else
55-
{
56-
<MudText>Scan Now</MudText>
57-
}
58-
</MudButton>
59-
</ToolBarContent>
60-
<HeaderContent>
61-
<MudTh>Repository</MudTh>
62-
<MudTh>Violations</MudTh>
63-
</HeaderContent>
64-
<RowTemplate>
65-
<MudTd DataLabel="Repository">
66-
<MudLink Href="@context.Url" Target="_blank">@context.Name</MudLink>
67-
</MudTd>
68-
<MudTd DataLabel="Violations">
69-
@foreach (var policy in context.ViolatedPolicies)
70-
{
71-
<MudChip T="string" Text="@policy" Color="Color.Error" Size="Size.Small" />
72-
}
73-
</MudTd>
74-
</RowTemplate>
75-
<PagerContent>
76-
<MudTablePager />
77-
</PagerContent>
78-
</MudTable>
79-
</MudPaper>
15+
@if (_viewModel == null)
16+
{
17+
<FluentProgress Ring="true" />
18+
}
19+
else
20+
{
21+
<div class="kpi-grid">
22+
<FluentCard class="kpi-card">
23+
<div class="kpi-title">
24+
<FluentIcon Icon="@(Microsoft.FluentUI.AspNetCore.Components.Icons.Regular.Size24.ChartMultiple)" />
25+
<span>Overall Compliance</span>
26+
</div>
27+
<div class="kpi-value">@($"{_viewModel.CompliancePercentage:F2}%")</div>
28+
</FluentCard>
29+
30+
<FluentCard class="kpi-card">
31+
<div class="kpi-title">
32+
<FluentIcon Icon="@(Microsoft.FluentUI.AspNetCore.Components.Icons.Regular.Size24.Apps)" />
33+
<span>Total Repositories</span>
34+
</div>
35+
<div class="kpi-value">@_viewModel.TotalRepositories</div>
36+
</FluentCard>
37+
38+
<FluentCard class="kpi-card compliant">
39+
<div class="kpi-title">
40+
<FluentIcon Icon="@(Microsoft.FluentUI.AspNetCore.Components.Icons.Regular.Size24.ShieldCheckmark)" />
41+
<span>Compliant</span>
42+
</div>
43+
<div class="kpi-value">@_viewModel.CompliantRepositories</div>
44+
</FluentCard>
45+
46+
<FluentCard class="kpi-card non-compliant">
47+
<div class="kpi-title">
48+
<FluentIcon Icon="@(Microsoft.FluentUI.AspNetCore.Components.Icons.Regular.Size24.ShieldError)" />
49+
<span>Non-Compliant</span>
50+
</div>
51+
<div class="kpi-value">@_viewModel.NonCompliantRepositoriesCount</div>
52+
</FluentCard>
53+
</div>
54+
55+
<FluentCard Class="mt-4" Style="padding: 16px; margin-top: 2rem;">
56+
<FluentStack Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center" Style="width: 100%;">
57+
<FluentStack Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center">
58+
<FluentIcon Icon="@(Microsoft.FluentUI.AspNetCore.Components.Icons.Regular.Size24.ShieldError)" />
59+
<h3 style="margin-left: 8px; margin-bottom: 0;">Non-Compliant Repositories</h3>
60+
</FluentStack>
61+
62+
<FluentSpacer />
63+
64+
<FluentStack Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center">
65+
<FluentTextField @bind-Value="_repositoryFilter" Placeholder="Filter by name..." Immediate="true" Style="width: 250px;"/>
66+
<FluentButton Id="scan-button" Appearance="Appearance.Accent" OnClick="StartScan" Disabled="@_isScanning" Style="margin-left: 16px;">
67+
@if (_isScanning)
68+
{
69+
<FluentProgress Ring="true" />
70+
<span>Scanning...</span>
71+
}
72+
else
73+
{
74+
<span>Scan Now</span>
75+
}
76+
</FluentButton>
77+
</FluentStack>
78+
</FluentStack>
79+
80+
@if (GetFilteredRepositories().Any())
81+
{
82+
<FluentDataGrid Items="@GetFilteredRepositories().AsQueryable()" TGridItem="NonCompliantRepositoryViewModel" Striped="true" Style="margin-top: 16px;">
83+
<TemplateColumn Title="Repository">
84+
<FluentAnchor Href="@context.Url" Target="_blank" Appearance="Appearance.Hypertext">@context.Name</FluentAnchor>
85+
</TemplateColumn>
86+
<TemplateColumn Title="Violations">
87+
<FluentStack Wrap="true">
88+
@foreach (var policy in context.ViolatedPolicies)
89+
{
90+
<FluentBadge Appearance="Appearance.Accent" Style="margin: 2px;">@policy</FluentBadge>
91+
}
92+
</FluentStack>
93+
</TemplateColumn>
94+
</FluentDataGrid>
95+
}
96+
else
97+
{
98+
<div style="padding: 24px; text-align: center;">
99+
<p>All repositories are compliant or match the current filter.</p>
100+
</div>
101+
}
102+
</FluentCard>
103+
}
104+
105+
<style>
106+
.kpi-grid {
107+
display: grid;
108+
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
109+
gap: 1rem;
110+
margin-top: 1rem;
111+
}
112+
113+
.kpi-card {
114+
padding: 16px;
115+
display: flex;
116+
flex-direction: column;
117+
justify-content: space-between;
118+
}
119+
120+
.kpi-title {
121+
display: flex;
122+
align-items: center;
123+
gap: 8px;
124+
font-size: 1rem;
125+
color: var(--neutral-foreground-rest);
126+
}
127+
128+
.kpi-value {
129+
font-size: 2.5rem;
130+
font-weight: 600;
131+
margin-top: 16px;
132+
text-align: right;
133+
}
134+
135+
.compliant {
136+
border-left: 4px solid var(--success-fill-rest);
137+
}
138+
139+
.non-compliant {
140+
border-left: 4px solid var(--error-fill-rest);
141+
}
142+
</style>
80143

81144
@code {
82145
private DashboardViewModel? _viewModel;
83-
private string _nameFilter = string.Empty;
84146
private bool _isScanning = false;
147+
private string _repositoryFilter = string.Empty;
85148

86-
protected override async Task OnInitializedAsync()
149+
private IEnumerable<NonCompliantRepositoryViewModel> GetFilteredRepositories()
87150
{
88-
await LoadDashboardData();
151+
if (_viewModel?.NonCompliantRepositories == null)
152+
{
153+
return Enumerable.Empty<NonCompliantRepositoryViewModel>();
154+
}
155+
156+
return _viewModel.NonCompliantRepositories
157+
.Where(r => string.IsNullOrWhiteSpace(_repositoryFilter) || r.Name.Contains(_repositoryFilter, StringComparison.OrdinalIgnoreCase));
89158
}
90159

91-
private bool FilterFunc(NonCompliantRepositoryViewModel repo)
160+
protected override async Task OnInitializedAsync()
92161
{
93-
if (string.IsNullOrWhiteSpace(_nameFilter))
94-
return true;
95-
if (repo.Name.Contains(_nameFilter, StringComparison.OrdinalIgnoreCase))
96-
return true;
97-
return false;
162+
await LoadDashboardData();
98163
}
99164

100165
private async Task LoadDashboardData()
101166
{
102-
_viewModel = await DashboardService.GetDashboardViewModelAsync(_nameFilter);
167+
_viewModel = await DashboardService.GetDashboardViewModelAsync(string.Empty);
103168
}
104169

105170
private async Task StartScan()

10xGitHubPolicies.App/Pages/_Host.cshtml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@
99
<meta charset="utf-8" />
1010
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
1111
<base href="~/" />
12-
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
1312
<link href="css/site.css" rel="stylesheet" />
1413
<link href="10xGitHubPolicies.App.styles.css" rel="stylesheet" />
1514
<link rel="icon" type="image/png" href="favicon.png"/>
16-
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
17-
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
15+
<link href="_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css" rel="stylesheet" />
1816
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
1917
</head>
2018
<body>
@@ -32,6 +30,6 @@
3230
</div>
3331

3432
<script src="_framework/blazor.server.js"></script>
35-
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
33+
<script type="module" src="_content/Microsoft.FluentUI.AspNetCore.Components/Microsoft.FluentUI.AspNetCore.Components.lib.module.js"></script>
3634
</body>
3735
</html>

10xGitHubPolicies.App/Program.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
using Microsoft.AspNetCore.Components.Web;
1313
using Microsoft.EntityFrameworkCore;
1414
using Microsoft.Extensions.Options;
15-
using MudBlazor.Services;
15+
using Microsoft.FluentUI.AspNetCore.Components;
1616

1717
var builder = WebApplication.CreateBuilder(args);
1818
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
@@ -33,7 +33,7 @@
3333

3434
builder.Services.AddRazorPages();
3535
builder.Services.AddServerSideBlazor();
36-
builder.Services.AddMudServices();
36+
builder.Services.AddFluentUIComponents();
3737
builder.Services.AddScoped<IDashboardService, DashboardService>();
3838

3939
builder.Services.AddEndpointsApiExplorer();
Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
@inherits LayoutComponentBase
22

3-
<MudThemeProvider />
4-
<MudDialogProvider />
5-
<MudSnackbarProvider />
6-
<MudPopoverProvider />
7-
8-
<MudLayout>
9-
<MudAppBar Elevation="1">
10-
<MudText Typo="Typo.h6" Class="ml-4">10x GitHub Policy Enforcer</MudText>
11-
<MudSpacer />
12-
</MudAppBar>
13-
<MudMainContent>
14-
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="my-4">
15-
@Body
16-
</MudContainer>
17-
</MudMainContent>
18-
</MudLayout>
3+
<FluentThemeProvider>
4+
<FluentLayout>
5+
<FluentHeader>
6+
<div style="display: flex; align-items: center; padding: 0 24px;">
7+
<FluentIcon Icon="@(Microsoft.FluentUI.AspNetCore.Components.Icons.Regular.Size28.ShieldCheckmark)" style="font-size: 28px;" />
8+
<span style="margin-left: 12px; font-size: var(--type-ramp-plus-2-font-size);">10x GitHub Policy Enforcer</span>
9+
</div>
10+
<FluentSpacer />
11+
<FluentThemeSwitcher />
12+
</FluentHeader>
13+
<FluentStack Orientation="Orientation.Vertical" Style="width: 100%;">
14+
<FluentBodyContent>
15+
<div class="content" style="padding: 24px;">
16+
@Body
17+
</div>
18+
</FluentBodyContent>
19+
</FluentStack>
20+
</FluentLayout>
21+
</FluentThemeProvider>

10xGitHubPolicies.App/_Imports.razor

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@
88
@using Microsoft.JSInterop
99
@using _10xGitHubPolicies.App
1010
@using _10xGitHubPolicies.App.Shared
11-
@using MudBlazor
11+
@using Microsoft.FluentUI.AspNetCore.Components
12+
@using Microsoft.FluentUI.AspNetCore.Components.Utilities
13+
@using Microsoft.FluentUI.AspNetCore.Components.Icons

AGENTS.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ You are a senior Blazor and .NET developer and an expert in `C#`, `ASP.NET Core`
5151
- Use `C#`'s expressive syntax (e.g., null-conditional operators, string interpolation)
5252
- Use `var` for implicit typing when the type is obvious.
5353

54-
### Frontend Development (Blazor & MudBlazor)
55-
- Use `MudBlazor` components to build the UI for a consistent look and feel.
56-
- Adhere to a consistent styling and theming strategy using `MudBlazor`'s theme provider.
54+
### Frontend Development (Blazor & Fluent UI)
55+
- Use `Microsoft.FluentUI.AspNetCore.Components` to build the UI for a consistent look and feel.
56+
- Adhere to a consistent styling and theming strategy using Fluent UI's theme provider.
5757
- Manage application state carefully. For complex state, consider using a cascading parameter-based state container.
5858

5959
### Error Handling and Validation

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## 0.2
9+
10+
### Changed
11+
- **UI Framework**: Replaced `MudBlazor` with `Microsoft.FluentUI.AspNetCore.Components` for the entire web UI. This includes updating all components, layouts, and styling to align with the Fluent UI design system.
12+
13+
### Dependencies
14+
- Replaced `MudBlazor` with `Microsoft.FluentUI.AspNetCore.Components` and `Microsoft.FluentUI.AspNetCore.Components.Icons`.
15+
16+
---
17+
818
## 0.1
919

1020
### Added

0 commit comments

Comments
 (0)