A small customer relationship management (CRM) web application built with ASP.NET Core 8 and Blazor (interactive server). It supports multiple users (team or solo use): sign-in with email and password, then manage customers, opportunities, interactions, and support tickets with threaded ticket responses. Administrators can create additional users from the Users screen.
The dashboard gives a high-level overview with a sidebar for navigating between customers, opportunities, interactions, support tickets, and user administration.
- Authentication — Cookie-based sign-in backed by the
Userstable (passwords stored as PBKDF2 hashes viaPasswordHasher). - Password visibility — On Sign in and on Admin → Users when creating a user, the password field includes an in-field eye icon to show or hide what you type. Toggle uses Blazor interactive server and inline SVG (no separate icon font required).
- Roles —
AdminandAgent(AppRoles). Admins see Users in the nav and can assign support tickets to agents on ticket detail; agents see a read-only note for assignment. - Dashboard — High-level counts (customers, open opportunities, open tickets, recent interactions).
- CRM areas — Customers (CRUD + detail with related counts), Opportunities (CRUD, customer + optional assigned rep), Interactions (log and filter by customer), Support tickets (create, list, detail with status updates and responses).
- Database — Entity Framework Core with SQL Server; schema matches the CRM ERD (GUID keys, nullable
Interactions.OpportunityId, etc.).
| Requirement | Notes |
|---|---|
| .NET 8 SDK | Required to build and run the app. Verify with dotnet --version (8.x). |
| Docker & Docker Compose | Used to run SQL Server 2022 locally. Optional if you already have SQL Server and a matching database. |
| SQL Server | Default setup expects localhost:1433, database SampleCRMDB, login sa (see appsettings.json and docker-compose.yml). |
Optional:
- dotnet-ef — For creating new migrations. This repo includes a local tool manifest under
.config/dotnet-tools.json; usedotnet tool restorethendotnet tool run dotnet-ef(see below).
From the repository root:
docker compose up -dWait until the mssql service is healthy. The mssql-init service creates the SampleCRMDB database if it does not exist.
Default SQL credentials (development only):
- User:
sa - Password:
SampleCRM_Sa1! - Port:
1433 - Database:
SampleCRMDB
Change these in production and update ConnectionStrings:DefaultConnection accordingly.
dotnet restore
dotnet runOr open the project in Visual Studio / Rider / VS Code and run the https or http profile.
Typical URLs (see Properties/launchSettings.json):
- HTTPS:
https://localhost:7273 - HTTP:
http://localhost:5179
On first run, the app applies EF Core migrations and seeds a default admin if no users exist:
| Field | Value |
|---|---|
admin@samplecrm.local |
|
| Password | Admin123! |
Click the eye inside the password box to reveal or mask the password. The same control appears when an admin sets a new user’s password on Admin → Users.
Use Admin → Users (admin only) to create more accounts (Admin or Agent).
- Connection string:
ConnectionStrings:DefaultConnectionin appsettings.json and appsettings.Development.json. - Cookie name / session: Configured in Program.cs (
SampleCRM.Auth, sliding 8-hour expiration). - Authorization: A fallback policy requires an authenticated user; Login and Error allow anonymous access.
- Sign-in page render mode: Login.razor uses
@rendermode InteractiveServerso the password show/hide control can run Blazor events while the form still posts to AccountController for authentication.
For secrets in real deployments, prefer User Secrets, environment variables, or a secret store—not committed JSON files.
The app uses SQL Server with database SampleCRMDB. Tables and columns are created by EF Core migrations; names and types below match the model in Data/ApplicationDbContext.cs and Data/Entities/.
erDiagram
Customers ||--o{ Opportunities : has
Customers ||--o{ Interactions : has
Customers ||--o{ SupportTickets : has
Users ||--o{ Interactions : creates
Users ||--o{ Opportunities : assigned_to
Users ||--o{ SupportTickets : assigned_to
Users ||--o{ TicketResponses : creates
Opportunities ||--o{ Interactions : has
SupportTickets ||--o{ TicketResponses : has
Customers {
uniqueidentifier CustomerId PK
nvarchar FirstName
nvarchar LastName
nvarchar Email
nvarchar Phone
nvarchar CompanyName
datetime2 CreatedAt
datetime2 UpdatedAt
}
Users {
uniqueidentifier UserId PK
nvarchar Name
nvarchar Email
nvarchar Role
bit Active
datetime2 CreatedAt
nvarchar PasswordHash
}
Opportunities {
uniqueidentifier OpportunityId PK
uniqueidentifier CustomerId FK
nvarchar Title
nvarchar Status
decimal ExpectedValue
date CloseDate
uniqueidentifier AssignedRepId FK
datetime2 CreatedAt
datetime2 UpdatedAt
}
Interactions {
uniqueidentifier InteractionId PK
uniqueidentifier CustomerId FK
uniqueidentifier OpportunityId FK
uniqueidentifier UserId FK
nvarchar Type
nvarchar Subject
nvarchar Details
datetime2 Timestamp
}
SupportTickets {
uniqueidentifier TicketId PK
uniqueidentifier CustomerId FK
nvarchar Subject
nvarchar Description
nvarchar Status
nvarchar Priority
uniqueidentifier AssignedAgentId FK
datetime2 CreatedAt
datetime2 UpdatedAt
datetime2 ClosedAt
}
TicketResponses {
uniqueidentifier ResponseId PK
uniqueidentifier TicketId FK
uniqueidentifier UserId FK
nvarchar Message
datetime2 Timestamp
}
- Context:
ApplicationDbContextin Data/ApplicationDbContext.cs. - Entities: Data/Entities/ (
Customer,User,Opportunity,Interaction,SupportTicket,TicketResponse). - Migrations: Data/Migrations/.
- Startup: DbInitializer runs
MigrateAsync()then seeds the admin user when the database is empty.
cd /path/to/SampleCRMApp
dotnet tool restore
dotnet tool run dotnet-ef migrations add YourMigrationName --output-dir Data/MigrationsDesign-time factory: Data/ApplicationDbContextFactory.cs (uses the same dev connection string as Docker defaults).
SampleCRMApp/
├── Components/ # Blazor UI (pages, layout, routes)
│ ├── Layout/
│ ├── Pages/ # Dashboard, Customers, Opportunities, Interactions, Tickets, Admin/Users, Account/Login
│ └── Routes.razor # AuthorizeRouteView + redirects
├── Controllers/ # AccountController (login POST, logout GET)
├── Data/ # DbContext, entities, migrations, seeding
├── Services/ # Auth state provider, IUserContext
├── wwwroot/ # Static assets (CSS, Bootstrap)
├── Program.cs # DI, pipeline, cookie auth, Blazor
├── docker-compose.yml # SQL Server + DB init
└── appsettings*.json
- Default SQL and app passwords are for local development only.
- Login uses anti-forgery tokens; MVC is registered with
AddControllersWithViews()so[ValidateAntiForgeryToken]works onAccountController. - HTTPS is recommended; the dev profile enables both HTTP and HTTPS.
| Issue | What to check |
|---|---|
| Cannot connect to SQL | Docker is running, docker compose ps, port 1433 not used by another instance, firewall. |
| Login / antiforgery errors | Ensure you are on a current build; controllers use AddControllersWithViews(). |
| Empty database / no tables | Run the app once so DbInitializer runs migrations; check Docker logs for SQL errors. |
| Wrong URL | Match launchSettings.json or the URL printed in the console when you dotnet run. |
No license is specified in this repository; add one if you intend to distribute or open-source the project.
