Skip to content

nathan5580/CopilotDemo

Repository files navigation

CopilotDemo

A small, conventions-correct .NET 10 app that lists Active Directory / LDAP users in a co-hosted Blazor WebAssembly UI styled with Tailwind CSS v4.

.NET 10 License: MIT Build

CopilotDemo

What is this

CopilotDemo is a clean, single-feature directory browser: search and filter a list of users, then open a detail panel showing their attributes and group memberships. It runs zero-config on seeded in-memory data, and can connect to a real LDAP / Active Directory server by flipping one config value.

Its real purpose is to be a polished, conventions-correct playground for demonstrating how AI assistants (Claude, GitHub Copilot) build features, write docs, debug, and add tests on a realistic .NET codebase. The demo runbook lives in DEMO.md: a minute-marked timeline of acts (build a feature, document, debug, test, extend, review).

Features

  • Co-hosted Blazor WebAssembly served from the same origin as the API. One dotnet run starts everything.
  • Zero-config seed data: 40+ realistic users across 6 departments and 8 groups, with a few disabled accounts to exercise every UI state. No database, no AD, no setup.
  • Live search (debounced) across display name, email, and username, plus a department filter.
  • Sortable user table with hash-colored initials avatars, department chips, and keyboard-focusable rows.
  • Slide-in detail drawer with the full attribute set, an enabled / disabled badge, and group memberships as chips. Focus-trapped, Esc to close.
  • Pagination wired to the API (prev / next, page X of Y).
  • Light / dark theme toggle, persisted to localStorage and applied before first paint to avoid a flash.
  • Three states everywhere: loading skeletons, empty state, and an error state with retry.
  • Responsive: the table collapses to cards on small screens.
  • Multi-page shell: Home, Directory, and About pages with a sticky top nav and a linked footer (App and Resources columns, live data-source indicator).
  • Two directory providers behind one IDirectoryService abstraction: in-memory seed data (default) and real LDAP.
  • OpenAPI + Scalar API reference at /docs, plus a /health endpoint and structured Serilog logging.

Screens

Home User detail drawer
Home page User detail drawer
Dark mode About page
Directory in dark mode About page

Architecture

The solution is split into thin, dependency-isolated projects. The API project (Microsoft.NET.Sdk.Web) hosts the REST endpoints and serves the compiled Blazor WebAssembly client from the same origin, so there is no CORS to configure and no second process to run. The directory data is fetched through an IDirectoryService abstraction whose concrete provider (in-memory or LDAP) is chosen by configuration.

The shared Contracts project holds only record types and has zero dependencies. It is the only shared project the WASM client references, which keeps the server-only LDAP library out of the browser bundle. The server-only Directory project owns the providers and the LDAP client.

Co-hosting is wired in CopilotDemo.API middleware in this order: UseBlazorFrameworkFiles() and UseStaticFiles() serve the WASM and static assets, MapControllers() serves /api/*, and MapFallbackToFile("index.html") returns the client shell for any unmatched route so client-side navigation survives a refresh.

CopilotDemo/
├── Applications/
│   ├── CopilotDemo.API/              # ASP.NET Core 10 Web API, co-hosts the WASM client
│   │   ├── Controllers/              # UsersController
│   │   ├── Extensions/               # ServiceExtensions, MiddlewareExtensions, ExceptionMiddleware
│   │   ├── GlobalUsings.cs, Program.cs
│   │   └── appsettings.json, appsettings.Development.json
│   └── CopilotDemo.Web/              # Blazor WebAssembly client
│       ├── Layout/                   # MainLayout, NavMenu
│       ├── Pages/                    # Home, Users
│       ├── Components/               # UserTable, UserDetailDrawer, SearchBar, Pagination, states, ...
│       ├── Services/                 # DirectoryApiClient (typed HttpClient), ThemeService, ...
│       ├── Styles/app.css            # Tailwind v4 input
│       ├── wwwroot/                  # index.html, css/ (built), favicon.svg, js/
│       └── package.json              # Tailwind build scripts
├── Shared/
│   ├── CopilotDemo.Shared.Contracts/ # records shared API <-> Web, zero dependencies
│   └── CopilotDemo.Shared.Directory/ # IDirectoryService + InMemory/Ldap providers (server only)
├── Tests/
│   ├── CopilotDemo.Shared.Directory.Tests/   # unit: in-memory provider search/filter/paging/get
│   └── CopilotDemo.API.Tests/                # integration: endpoints + co-host fallback
├── Directory.Build.props
├── Directory.Packages.props
├── CopilotDemo.slnx
├── README.md, DEMO.md, CLAUDE.md, LICENSE
└── .github/                          # CI workflow, dependabot, issue/PR templates

Quickstart

Prerequisites: the .NET 10 SDK (built and verified against 10.0.102).

git clone https://github.com/nathan5580/CopilotDemo.git
cd CopilotDemo
dotnet run --project Applications/CopilotDemo.API

Then open http://localhost:5179 in your browser.

That is the whole setup. The app runs zero-config on the seeded in-memory directory, so there is nothing to install, no database to migrate, and no AD to connect. The API reference is at http://localhost:5179/docs.

Connecting a real Active Directory

The default provider is the in-memory seed set. To query a live LDAP / Active Directory server instead, set Directory:Provider to Ldap and fill in the connection block (in appsettings.json, appsettings.Development.json, environment variables, or user secrets):

{
  "Directory": {
    "Provider": "Ldap",
    "Ldap": {
      "Host": "dc01.contoso.com",
      "Port": 389,
      "UseSsl": false,
      "BaseDn": "DC=contoso,DC=com",
      "BindDn": "CN=svc-directory,OU=Service Accounts,DC=contoso,DC=com",
      "BindPassword": "your-service-account-password",
      "UserFilter": "(&(objectCategory=person)(objectClass=user))"
    }
  }
}

Keep secrets out of source control; prefer environment variables or dotnet user-secrets for BindPassword. Use Port 636 with "UseSsl": true for LDAPS.

Each user entry is mapped from these Active Directory attributes:

AD attribute Maps to Notes
objectGUID Id Falls back to the entry DN.
sAMAccountName UserName
displayName DisplayName Falls back to cn.
mail Email
title JobTitle
department Department Also feeds the filter dropdown.
telephoneNumber PhoneNumber
physicalDeliveryOfficeName OfficeLocation
userAccountControl Enabled The ACCOUNTDISABLE bit (0x2) cleared means enabled.
memberOf Groups The CN of each group DN.

Connection, bind, or search failures are wrapped in a DirectoryException and surfaced as HTTP 502 by the API.

Tech stack

Layer Tech Version
API ASP.NET Core (Microsoft.NET.Sdk.Web) .NET 10
Web Blazor WebAssembly (Microsoft.NET.Sdk.BlazorWebAssembly) 10.0.7
Styling Tailwind CSS (CSS-first, @tailwindcss/cli) v4
Directory Novell.Directory.Ldap.NETStandard (real) + seeded in-memory (default) 3.6.0
API docs OpenAPI (Microsoft.AspNetCore.OpenApi) + Scalar (Scalar.AspNetCore) at /docs 10.0.7 / 2.14.2
Logging Serilog (console + rolling file) 10.0.0
Tests xUnit + Moq + Microsoft.AspNetCore.Mvc.Testing + coverlet 2.9.3 / 4.20.72 / 10.0.6

The build runs with TreatWarningsAsErrors=true, so a successful build means zero warnings.

Running tests

dotnet test

This runs both suites: unit tests for the in-memory provider (search, department filter, paging math, get-by-id, distinct departments, provider selection) and integration tests that drive the real host via WebApplicationFactory (the user endpoints plus the co-host fallback that returns index.html for client routes).

Tailwind development

The committed wwwroot/css/app.css lets the app render straight after a clone, before npm has ever run. To edit styles, install the dev dependencies and run the watcher from the Web project:

cd Applications/CopilotDemo.Web
npm install
npm run css:watch     # rebuilds wwwroot/css/app.css on every change

Use npm run css:build for a one-shot minified bundle. The .NET build also runs css:build automatically (the BuildTailwind MSBuild target) once node_modules exists.

Contributing

Issues and pull requests are welcome. The repo ships issue templates and a pull request template. Please keep to the conventions in CLAUDE.md and .github/copilot-instructions.md, and make sure dotnet build is warning-free and dotnet test is green before opening a PR.

License

Released under the MIT License.

About

A .NET 10 co-hosted Blazor WebAssembly + Tailwind v4 directory browser for Active Directory / LDAP. A clean playground for demoing AI-assisted development.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors