diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000..4440c69 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,36 @@ + + + + true + $(MSBuildThisFileDirectory)Directory.Packages.props + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/IssueManager.slnx b/IssueManager.slnx new file mode 100644 index 0000000..1ec6787 --- /dev/null +++ b/IssueManager.slnx @@ -0,0 +1,12 @@ +{ + "$schema": "http://json.schemastore.org/solution-manifest-1.0.json", + "version": "0.1", + "name": "IssueManager", + "description": "Issue management application with modern architecture patterns and async/reactive workflows", + "projects": [ + "src/AppHost/AppHost.csproj", + "src/ServiceDefaults/ServiceDefaults.csproj", + "src/Api/Api.csproj", + "src/Web/Web.csproj" + ] +} diff --git a/global.json b/global.json new file mode 100644 index 0000000..9668f6e --- /dev/null +++ b/global.json @@ -0,0 +1,5 @@ +{ + "sdk": { + "version": "10.0.200-preview.0.26103.119" + } +} diff --git a/src/Api/Api.csproj b/src/Api/Api.csproj new file mode 100644 index 0000000..bea45e6 --- /dev/null +++ b/src/Api/Api.csproj @@ -0,0 +1,25 @@ + + + net10.0 + 14.0 + enable + enable + IssueManager.Api + false + + + + + + + + + + + + + + + + + diff --git a/src/Api/Program.cs b/src/Api/Program.cs new file mode 100644 index 0000000..7683a38 --- /dev/null +++ b/src/Api/Program.cs @@ -0,0 +1,36 @@ +using IssueManager.ServiceDefaults; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddServiceDefaults(); + +builder.Services.AddOpenApi(); + +var app = builder.Build(); + +app.UseHttpsRedirection(); +app.MapOpenApi(); + +var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; + +app.MapGet("/weatherforecast", () => +{ + var forecast = Enumerable.Range(1, 5).Select(index => + new WeatherForecast( + DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + Random.Shared.Next(-20, 55), + summaries[Random.Shared.Next(summaries.Length)] + )) + .ToArray(); + return forecast; +}) +.WithName("GetWeatherForecast"); + +app.MapHealthChecks("/health"); + +app.Run(); + +internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) +{ + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); +} diff --git a/src/AppHost/AppHost.csproj b/src/AppHost/AppHost.csproj new file mode 100644 index 0000000..190e9dc --- /dev/null +++ b/src/AppHost/AppHost.csproj @@ -0,0 +1,24 @@ + + + Exe + net10.0 + 14.0 + enable + enable + IssueManager.AppHost + + + + + + + + + + + + + + + + diff --git a/src/AppHost/Program.cs b/src/AppHost/Program.cs new file mode 100644 index 0000000..5ddc759 --- /dev/null +++ b/src/AppHost/Program.cs @@ -0,0 +1,15 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var mongodb = builder + .AddMongoDB("mongodb") + .AddDatabase("issuemanager"); + +var api = builder + .AddProject("api", "../Api/Api.csproj") + .WithReference(mongodb); + +builder + .AddProject("web", "../Web/Web.csproj") + .WithReference(api); + +builder.Build().Run(); diff --git a/src/ServiceDefaults/Extensions.cs b/src/ServiceDefaults/Extensions.cs new file mode 100644 index 0000000..0e53873 --- /dev/null +++ b/src/ServiceDefaults/Extensions.cs @@ -0,0 +1,27 @@ +namespace IssueManager.ServiceDefaults; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +/// +/// Extension methods for service defaults configuration. +/// +public static class Extensions +{ + /// + /// Adds default service configuration for Aspire services. + /// + public static IServiceCollection AddServiceDefaults(this IServiceCollection services) + { + services.AddServiceDiscovery(); + return services; + } + + /// + /// Adds default health check configuration. + /// + public static IHealthChecksBuilder AddDefaultHealthChecks(this IServiceCollection services) + { + return services.AddHealthChecks(); + } +} diff --git a/src/ServiceDefaults/ServiceDefaults.csproj b/src/ServiceDefaults/ServiceDefaults.csproj new file mode 100644 index 0000000..17dc94e --- /dev/null +++ b/src/ServiceDefaults/ServiceDefaults.csproj @@ -0,0 +1,20 @@ + + + net10.0 + 14.0 + enable + enable + IssueManager.ServiceDefaults + + + + + + + + + + + + + diff --git a/src/Web/App.razor b/src/Web/App.razor new file mode 100644 index 0000000..87b8e1e --- /dev/null +++ b/src/Web/App.razor @@ -0,0 +1,21 @@ +@namespace IssueManager.Web + + + + + + + + + + + + + + + + + + + + diff --git a/src/Web/Layout/MainLayout.razor b/src/Web/Layout/MainLayout.razor new file mode 100644 index 0000000..dd44886 --- /dev/null +++ b/src/Web/Layout/MainLayout.razor @@ -0,0 +1,26 @@ +@namespace IssueManager.Web.Layout +@inherits LayoutComponentBase +@implements IAsyncDisposable + +
+ + +
+
+ About +
+ +
+ @Body +
+
+
+ +@code { + async ValueTask IAsyncDisposable.DisposeAsync() + { + await Task.CompletedTask; + } +} diff --git a/src/Web/Layout/NavMenu.razor b/src/Web/Layout/NavMenu.razor new file mode 100644 index 0000000..3c6e8e1 --- /dev/null +++ b/src/Web/Layout/NavMenu.razor @@ -0,0 +1,21 @@ +@namespace IssueManager.Web.Layout + + + +@code { + +} diff --git a/src/Web/Pages/Home.razor b/src/Web/Pages/Home.razor new file mode 100644 index 0000000..de0c919 --- /dev/null +++ b/src/Web/Pages/Home.razor @@ -0,0 +1,12 @@ +@namespace IssueManager.Web.Pages +@page "/" + +Home + +

Welcome to IssueManager

+ +

This application demonstrates modern .NET architecture patterns with Aspire, Blazor, and MongoDB.

+ +@code { + +} diff --git a/src/Web/Program.cs b/src/Web/Program.cs new file mode 100644 index 0000000..3f2f0a1 --- /dev/null +++ b/src/Web/Program.cs @@ -0,0 +1,28 @@ +using IssueManager.ServiceDefaults; +using IssueManager.Web; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddServiceDefaults(); + +builder.Services.AddRazorComponents() + .AddInteractiveServerComponents(); + +var app = builder.Build(); + +if (!app.Environment.IsDevelopment()) +{ + app.UseExceptionHandler("/Error", createScopeForErrors: true); + app.UseHsts(); +} + +app.UseHttpsRedirection(); +app.UseStaticFiles(); +app.UseAntiforgery(); + +app.MapRazorComponents() + .AddInteractiveServerRenderMode(); + +app.MapHealthChecks("/health"); + +app.Run(); diff --git a/src/Web/Routes.razor b/src/Web/Routes.razor new file mode 100644 index 0000000..643d789 --- /dev/null +++ b/src/Web/Routes.razor @@ -0,0 +1,14 @@ +@namespace IssueManager.Web + + + + + + + Not found + +

Sorry, there's nothing at this address.

+
+
+
+ diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj new file mode 100644 index 0000000..720ae3c --- /dev/null +++ b/src/Web/Web.csproj @@ -0,0 +1,22 @@ + + + net10.0 + 14.0 + enable + enable + IssueManager.Web + false + + + + + + + + + + + + + + diff --git a/src/Web/_Imports.razor b/src/Web/_Imports.razor new file mode 100644 index 0000000..a53a6fe --- /dev/null +++ b/src/Web/_Imports.razor @@ -0,0 +1,7 @@ +@using System.Globalization +@using Microsoft.AspNetCore.Components +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using IssueManager.Web +@using IssueManager.Web.Layout +@using IssueManager.Web.Pages diff --git a/src/Web/wwwroot/app.css b/src/Web/wwwroot/app.css new file mode 100644 index 0000000..8c47727 --- /dev/null +++ b/src/Web/wwwroot/app.css @@ -0,0 +1,56 @@ +html, body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + margin: 0; + padding: 0; +} + +body { + background-color: #f5f5f5; +} + +.page { + display: flex; + height: 100vh; +} + +.sidebar { + width: 250px; + background-color: #f8f9fa; + border-right: 1px solid #dee2e6; + overflow-y: auto; +} + +main { + flex: 1; + overflow-y: auto; +} + +.content { + max-width: 1200px; + padding: 2rem; +} + +.top-row { + background-color: #fff; + border-bottom: 1px solid #dee2e6; + padding: 1rem; +} + +.top-row a { + color: #007bff; + text-decoration: none; +} + +.top-row a:hover { + text-decoration: underline; +} + +h1 { + color: #212529; + margin-top: 0; +} + +p { + color: #6c757d; + line-height: 1.6; +}