Compare commits

..

14 Commits

Author SHA1 Message Date
OlgunR
8933deec96 Add dark mode override for non-native DevExpress themes
Implements a dark mode override system for DevExpress Blazor themes lacking native dark support. Adds a JS function to toggle a dx-dark class on <html>, updates ThemeState to detect native dark themes, and applies targeted CSS variable overrides for consistent dark styling. Disables prerendering to ensure JS interop, and improves theme switching logic and documentation.
2026-05-05 16:41:15 +02:00
OlgunR
2010673eba Remove unused menu state and toggle logic from NavMenu
Removed the private menuOpen field and ToggleMenu() method from NavMenu.razor, as they are no longer needed for menu state management.
2026-04-30 15:41:05 +02:00
OlgunR
b75e7d730c Modernize sidebar with DxTreeView and new responsive styles
Refactor NavMenu to use DevExpress DxTreeView for navigation, replacing the old NavLink-based menu. Update sidebar and navigation row styling to use CSS variables, remove Bootstrap-specific and SVG icon CSS, and add a responsive hamburger menu for small screens. Improve dark mode support and overall maintainability.
2026-04-30 15:37:04 +02:00
OlgunR
075433c780 Improve spacing for Dark Mode button in top row
Wrapped the Dark Mode toggle button in a span with left margin
for better separation from the theme combo box. Added a new
.btn-gap CSS class to standardize button spacing in the top row.
2026-04-23 15:59:50 +02:00
OlgunR
35e39ff979 Add theme selection dropdown and refactor theme handling
Introduce a DxComboBox in MainLayout for selecting between multiple themes. Update ThemeState to manage the current theme, provide a list of available themes, and apply the selected theme via a new SetTheme method. Refactor dark mode handling to work with the new theme system, and ensure UI updates on theme or mode changes.
2026-04-23 15:44:26 +02:00
OlgunR
292ce02370 Refactor using statements for clearer layer boundaries
Clean up and reorganize using/import statements across the solution. Remove unnecessary DTO imports from Application and Infrastructure layers, and ensure Contracts DTOs are only referenced in API and BlazorWebApp layers. No business logic is changed; these updates improve code organization, reduce coupling, and clarify architectural separation between layers.
2026-04-23 13:55:05 +02:00
OlgunR
df4de5d5f5 Refactor imports and ensure enum type safety in Catalogs
Reorganize and deduplicate using directives in _Imports.razor for clarity and maintainability. Explicitly cast UpdateProcedure to CatalogUpdateProcedure in CatalogsGrid.razor to enforce type safety. Restore necessary DevExpress and DbFirst namespaces.
2026-04-23 13:54:50 +02:00
OlgunR
e4624c92ef Refactor DTOs: make public, add properties, clean up
Refactored DTO classes by removing unnecessary using statements and internal wrappers, making them public, and adding explicit properties with default values. Clarified namespaces and improved accessibility for use in API contracts or service layers. Added CatalogUpdateProcedure to CatalogWriteDto to specify update operation type.
2026-04-23 11:48:45 +02:00
OlgunR
b0d60461b4 Standardize DTO naming and namespaces for MassData
Refactored DTO class and namespace names from Massdata* to MassData* in DbFirst.Contracts. Updated all relevant application files to use the correct DTO namespaces for Catalogs and MassData. Adjusted _Imports.razor to include new DTO namespaces and removed unused usings. These changes improve code consistency and reduce namespace-related errors.
2026-04-23 11:41:10 +02:00
OlgunR
c45c1a69a7 Remove obsolete DTO classes and add Layouts folder
Deleted DTOs for catalogs, layouts, mass data, and dashboard info from both Application and BlazorWebApp.Models namespaces. Updated DbFirst.Application.csproj to include a new Layouts folder for future organization. No functional code changes in this commit.
2026-04-23 11:25:31 +02:00
OlgunR
b268e53e1f Add project references to Contracts and Domain projects
Added DbFirst.Contracts as a project reference to both DbFirst.Application and DbFirst.BlazorWebApp, and added DbFirst.Domain to DbFirst.Application. Also, a BOM was introduced in the BlazorWebApp project file. These changes enable shared use of contracts and domain types across projects.
2026-04-23 11:23:24 +02:00
OlgunR
a1fee6f5c0 Add initial empty DTO classes for Catalogs, Dashboards, etc.
Introduced six new internal DTO classes: CatalogReadDto, CatalogWriteDto, DashboardInfoDto, LayoutDto, MassdataReadDto, and MassdataWriteDto. Each class resides in its appropriate namespace and currently contains no properties or methods, serving as placeholders for future data transfer logic.
2026-04-23 11:22:58 +02:00
OlgunR
dbb39354ab Add DbFirst.Contracts project to solution
Added new DbFirst.Contracts project targeting .NET 8.0 with nullable and implicit usings enabled. Included a project reference to DbFirst.Domain and updated the solution file to register the new project with all build configurations.
2026-04-23 11:15:53 +02:00
OlgunR
2ffa389978 Update AutoMapper to 16.1.1 and adjust registration
Upgraded AutoMapper to version 16.1.1 across API, Application, and Infrastructure projects. Removed AutoMapper.Extensions.Microsoft.DependencyInjection where no longer needed. Updated AutoMapper registration in DependencyInjection.cs to use the new API. No other changes made.
2026-04-22 09:35:50 +02:00
61 changed files with 368 additions and 241 deletions

View File

@@ -1,6 +1,7 @@
using DbFirst.Application.Catalogs;
using DbFirst.Application.Catalogs.Commands;
using DbFirst.Application.Catalogs.Queries;
using DbFirst.Contracts.Catalogs;
using MediatR;
using Microsoft.AspNetCore.Mvc;

View File

@@ -1,8 +1,8 @@
using System.Text;
using DbFirst.Application.Repositories;
using DbFirst.Contracts.Layouts;
using DbFirst.Domain.Entities;
using Microsoft.AspNetCore.Mvc;
using DbFirst.Application.Layouts;
using System.Text;
namespace DbFirst.API.Controllers;

View File

@@ -1,6 +1,6 @@
using DbFirst.Application.MassData;
using DbFirst.Application.MassData.Commands;
using DbFirst.Application.MassData.Queries;
using DbFirst.Contracts.MassData;
using MediatR;
using Microsoft.AspNetCore.Mvc;

View File

@@ -1,8 +1,8 @@
using DevExpress.DashboardWeb;
using Microsoft.Data.SqlClient;
using System.Data;
using System.Text;
using System.Xml.Linq;
using DevExpress.DashboardWeb;
using Microsoft.Data.SqlClient;
namespace DbFirst.API.Dashboards;

View File

@@ -7,8 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageReference Include="AutoMapper" Version="16.1.1" />
<PackageReference Include="DevExpress.AspNetCore.Dashboard" Version="25.2.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.22" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.22" />

View File

@@ -1,4 +1,5 @@
using AutoMapper;
using DbFirst.Contracts.Catalogs;
using DbFirst.Domain.Entities;
namespace DbFirst.Application.Catalogs;

View File

@@ -1,3 +1,4 @@
using DbFirst.Contracts.Catalogs;
using MediatR;
namespace DbFirst.Application.Catalogs.Commands;

View File

@@ -1,5 +1,6 @@
using AutoMapper;
using DbFirst.Application.Repositories;
using DbFirst.Contracts.Catalogs;
using DbFirst.Domain.Entities;
using MediatR;

View File

@@ -1,3 +1,4 @@
using DbFirst.Contracts.Catalogs;
using MediatR;
namespace DbFirst.Application.Catalogs.Commands;

View File

@@ -1,7 +1,8 @@
using AutoMapper;
using DbFirst.Application.Repositories;
using DbFirst.Domain.Entities;
using DbFirst.Contracts.Catalogs;
using DbFirst.Domain;
using DbFirst.Domain.Entities;
using MediatR;
namespace DbFirst.Application.Catalogs.Commands;

View File

@@ -1,5 +1,6 @@
using AutoMapper;
using DbFirst.Application.Repositories;
using DbFirst.Contracts.Catalogs;
using MediatR;
namespace DbFirst.Application.Catalogs.Queries;

View File

@@ -1,3 +1,4 @@
using DbFirst.Contracts.Catalogs;
using MediatR;
namespace DbFirst.Application.Catalogs.Queries;

View File

@@ -1,5 +1,6 @@
using AutoMapper;
using DbFirst.Application.Repositories;
using DbFirst.Contracts.Catalogs;
using MediatR;
namespace DbFirst.Application.Catalogs.Queries;

View File

@@ -1,3 +1,4 @@
using DbFirst.Contracts.Catalogs;
using MediatR;
namespace DbFirst.Application.Catalogs.Queries;

View File

@@ -7,14 +7,18 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageReference Include="AutoMapper" Version="16.1.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
<PackageReference Include="MediatR" Version="14.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DbFirst.Contracts\DbFirst.Contracts.csproj" />
<ProjectReference Include="..\DbFirst.Domain\DbFirst.Domain.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Layouts\" />
</ItemGroup>
</Project>

View File

@@ -1,5 +1,4 @@
using Microsoft.Extensions.DependencyInjection;
using MediatR;
namespace DbFirst.Application;
@@ -7,7 +6,7 @@ public static class DependencyInjection
{
public static IServiceCollection AddApplication(this IServiceCollection services)
{
services.AddAutoMapper(typeof(DependencyInjection).Assembly);
services.AddAutoMapper(cfg => cfg.AddMaps(typeof(DependencyInjection).Assembly));
services.AddMediatR(cfg =>
cfg.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly));
return services;

View File

@@ -1,9 +0,0 @@
namespace DbFirst.Application.Layouts;
public class LayoutDto
{
public string LayoutType { get; set; } = string.Empty;
public string LayoutKey { get; set; } = string.Empty;
public string UserName { get; set; } = string.Empty;
public string LayoutData { get; set; } = string.Empty;
}

View File

@@ -1,3 +1,4 @@
using DbFirst.Contracts.MassData;
using MediatR;
namespace DbFirst.Application.MassData.Commands;

View File

@@ -1,5 +1,6 @@
using AutoMapper;
using DbFirst.Application.Repositories;
using DbFirst.Contracts.MassData;
using MediatR;
namespace DbFirst.Application.MassData.Commands;

View File

@@ -1,4 +1,5 @@
using AutoMapper;
using DbFirst.Contracts.MassData;
using DbFirst.Domain.Entities;
namespace DbFirst.Application.MassData;

View File

@@ -1,12 +0,0 @@
namespace DbFirst.Application.MassData;
public class MassDataReadDto
{
public int Id { get; set; }
public string CustomerName { get; set; } = string.Empty;
public decimal Amount { get; set; }
public string Category { get; set; } = string.Empty;
public bool StatusFlag { get; set; }
public DateTime AddedWhen { get; set; }
public DateTime? ChangedWhen { get; set; }
}

View File

@@ -1,5 +1,6 @@
using AutoMapper;
using DbFirst.Application.Repositories;
using DbFirst.Contracts.MassData;
using MediatR;
namespace DbFirst.Application.MassData.Queries;

View File

@@ -1,3 +1,4 @@
using DbFirst.Contracts.MassData;
using MediatR;
namespace DbFirst.Application.MassData.Queries;

View File

@@ -1,5 +1,6 @@
using AutoMapper;
using DbFirst.Application.Repositories;
using DbFirst.Contracts.MassData;
using MediatR;
namespace DbFirst.Application.MassData.Queries;

View File

@@ -1,3 +1,4 @@
using DbFirst.Contracts.MassData;
using MediatR;
namespace DbFirst.Application.MassData.Queries;

View File

@@ -24,6 +24,14 @@
<link rel="stylesheet" href="DbFirst.BlazorWebApp.styles.css" />
<link rel="icon" type="image/png" href="favicon.png" />
<script src="js/size-manager.js"></script>
<script>
window.setDxDarkOverride = function (enabled) {
if (enabled)
document.documentElement.classList.add('dx-dark');
else
document.documentElement.classList.remove('dx-dark');
};
</script>
<HeadOutlet />
</head>

View File

@@ -259,7 +259,7 @@ else
{
CatTitle = editModel.CatTitle,
CatString = editModel.CatString,
UpdateProcedure = editModel.UpdateProcedure
UpdateProcedure = (CatalogUpdateProcedure)editModel.UpdateProcedure
};
try

View File

@@ -1,15 +1,23 @@
@inherits LayoutComponentBase
@implements IDisposable
@inject ThemeState ThemeState
@inject IJSRuntime JS
<div class="page @(ThemeState.IsDarkMode ? "app-dark" : "app-light")">
<div class="page @(ThemeState.IsDarkMode ? "app-dark" : "app-light") @(ThemeState.IsNativeDarkTheme ? "native-dark" : "")">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<DxButton Text="@(ThemeState.IsDarkMode ? "Dark Mode aus" : "Dark Mode an")" Click="ToggleTheme" />
<DxComboBox Data="@ThemeState.AvailableThemes"
Value="@ThemeState.CurrentThemeName"
ValueChanged="@((string t) => ThemeState.SetTheme(t))"
style="width: 130px;" />
<span style="margin-left: 12px;">
<DxButton Text="@(ThemeState.IsDarkMode ? "Dark Mode aus" : "Dark Mode an")"
Click="ToggleTheme" />
</span>
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
</div>
@@ -26,9 +34,43 @@
</div>
@code {
private bool _isInteractive;
protected override void OnInitialized()
{
ThemeState.OnChange += StateHasChanged;
ThemeState.OnChange += OnThemeChanged;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
_isInteractive = true;
}
await ApplyDxDarkOverrideAsync();
}
private async void OnThemeChanged()
{
StateHasChanged();
if (_isInteractive)
{
await ApplyDxDarkOverrideAsync();
}
}
private async Task ApplyDxDarkOverrideAsync()
{
if (!_isInteractive) return;
try
{
bool needsOverride = ThemeState.IsDarkMode && !ThemeState.IsNativeDarkTheme;
await JS.InvokeVoidAsync("setDxDarkOverride", needsOverride);
}
catch (JSException)
{
// JS-Funktion noch nicht verfügbar kein Circuit-Crash
}
}
private void ToggleTheme()
@@ -38,6 +80,6 @@
public void Dispose()
{
ThemeState.OnChange -= StateHasChanged;
ThemeState.OnChange -= OnThemeChanged;
}
}

View File

@@ -18,11 +18,13 @@ main {
}
.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
background-color: var(--dx-color-surface-container, #f4f4f4);
border-right: 1px solid var(--dx-color-outline-variant, #e0e0e0);
}
.page.app-dark .sidebar {
background-image: linear-gradient(180deg, #171717 0%, #0f2a46 70%);
background-color: var(--dx-color-surface-container, #1e1e1e);
border-right-color: var(--dx-color-outline-variant, #333);
}
.top-row {

View File

@@ -1,36 +1,24 @@
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">DbFirst.BlazorWebApp</a>
</div>
<div class="nav-brand-row">
<a class="nav-brand-link" href="">DbFirst</a>
<button class="nav-toggle-btn" @onclick="ToggleMenu" title="Navigation menu">&#9776;</button>
</div>
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="catalogs">
<span class="bi bi-collection-nav-menu" aria-hidden="true"></span> Catalogs
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="dashboards">
<span class="bi bi-speedometer-nav-menu" aria-hidden="true"></span> Dashboards
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="massdata">
<span class="bi bi-table-nav-menu" aria-hidden="true"></span> MassData
</NavLink>
</div>
</nav>
<div class="nav-scrollable @(menuOpen ? "nav-open" : "")">
<DxTreeView CssClass="sidebar-tree">
<Nodes>
<DxTreeViewNode Text="Home" NavigateUrl="/" IconCssClass="dxi dxi-home" />
<DxTreeViewNode Text="Data Management" Expanded="true">
<Nodes>
<DxTreeViewNode Text="Catalogs" NavigateUrl="/catalogs" IconCssClass="dxi dxi-folder" />
<DxTreeViewNode Text="Dashboards" NavigateUrl="/dashboards" IconCssClass="dxi dxi-chart-bar" />
<DxTreeViewNode Text="Mass Data" NavigateUrl="/massdata" IconCssClass="dxi dxi-table" />
</Nodes>
</DxTreeViewNode>
</Nodes>
</DxTreeView>
</div>
@code {
private bool menuOpen = false;
private void ToggleMenu() => menuOpen = !menuOpen;
}

View File

@@ -1,117 +1,52 @@
.navbar-toggler {
appearance: none;
cursor: pointer;
width: 3.5rem;
height: 2.5rem;
color: white;
position: absolute;
top: 0.5rem;
right: 1rem;
border: 1px solid rgba(255, 255, 255, 0.1);
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
}
.navbar-toggler:checked {
background-color: rgba(255, 255, 255, 0.5);
}
.top-row {
.nav-brand-row {
height: 3.5rem;
background-color: rgba(0,0,0,0.4);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 1rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
}
.navbar-brand {
font-size: 1.1rem;
.nav-brand-link {
font-size: 1.05rem;
font-weight: 600;
text-decoration: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.bi {
display: inline-block;
position: relative;
width: 1.25rem;
height: 1.25rem;
margin-right: 0.75rem;
top: -1px;
background-size: cover;
}
.bi-house-door-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
}
.bi-plus-square-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
}
.bi-list-nested-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
}
.bi-collection-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' viewBox='0 0 16 16'%3E%3Cpath d='M2 3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 .5.5v1H2V3z'/%3E%3Cpath d='M2 5h12v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5z'/%3E%3C/svg%3E");
}
.bi-speedometer-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' viewBox='0 0 16 16'%3E%3Cpath d='M1 11a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v4zm5 0a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1v8zm5 0a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1h-2a1 1 0 0 0-1 1v2z'/%3E%3C/svg%3E");
}
.bi-table-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' viewBox='0 0 16 16'%3E%3Cpath d='M1 2a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2zm1 1v2h12V3H2zm12 3H2v2h12V6zm0 3H2v2h12V9zm0 3H2v1h12v-1z'/%3E%3C/svg%3E");
}
.nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
.nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:last-of-type {
padding-bottom: 1rem;
}
.nav-item ::deep .nav-link {
color: #d7d7d7;
background: none;
border: none;
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
width: 100%;
}
.nav-item ::deep a.active {
background-color: rgba(255,255,255,0.37);
color: white;
}
.nav-item ::deep .nav-link:hover {
background-color: rgba(255,255,255,0.1);
color: white;
.nav-toggle-btn {
display: block;
background: none;
border: none;
font-size: 1.4rem;
cursor: pointer;
padding: 0.25rem 0.5rem;
line-height: 1;
}
.nav-scrollable {
display: none;
}
.navbar-toggler:checked ~ .nav-scrollable {
display: block;
.nav-scrollable.nav-open {
display: block;
}
.sidebar-tree {
width: 100%;
}
@media (min-width: 641px) {
.navbar-toggler {
.nav-toggle-btn {
display: none;
}
.nav-scrollable {
/* Never collapse the sidebar for wide screens */
display: block;
/* Allow sidebar to scroll for tall menus */
height: calc(100vh - 3.5rem);
overflow-y: auto;
}
}

View File

@@ -1,4 +1,5 @@
@rendermode InteractiveServer
@rendermode @(new InteractiveServerRenderMode(prerender: false))
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">

View File

@@ -1,21 +1,28 @@
@using System.Net.Http
@using System.Net.Http.Json
@using System.Text.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Rendering
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using Microsoft.AspNetCore.SignalR.Client
@using Microsoft.JSInterop
@using Microsoft.Extensions.Options
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using DbFirst.BlazorWebApp
@using DbFirst.BlazorWebApp.Components
@using DbFirst.BlazorWebApp.Models
@using DbFirst.BlazorWebApp.Models.Grid
@using DbFirst.BlazorWebApp.Services
@using DbFirst.Contracts.Catalogs
@using DbFirst.Contracts.Dashboards
@using DbFirst.Contracts.MassData
@using DbFirst.Contracts.Layouts
@using DbFirst.Domain
@using DevExpress.Blazor
@using DevExpress.DashboardBlazor
@using DevExpress.DashboardWeb
@using DevExpress.Data.Filtering
@using Microsoft.Extensions.Options
@using DevExpress.Data.Filtering

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
@@ -18,4 +18,8 @@
<Folder Include="wwwroot\images\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DbFirst.Contracts\DbFirst.Contracts.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,12 +0,0 @@
namespace DbFirst.BlazorWebApp.Models;
public class CatalogReadDto
{
public int Guid { get; set; }
public string CatTitle { get; set; } = string.Empty;
public string CatString { get; set; } = string.Empty;
public string AddedWho { get; set; } = string.Empty;
public DateTime AddedWhen { get; set; }
public string? ChangedWho { get; set; }
public DateTime? ChangedWhen { get; set; }
}

View File

@@ -1,8 +0,0 @@
namespace DbFirst.BlazorWebApp.Models;
public class CatalogWriteDto
{
public string CatTitle { get; set; } = string.Empty;
public string CatString { get; set; } = string.Empty;
public int UpdateProcedure { get; set; }
}

View File

@@ -1,9 +0,0 @@
namespace DbFirst.BlazorWebApp.Models;
public class MassDataWriteDto
{
public string CustomerName { get; set; } = string.Empty;
public decimal Amount { get; set; }
public string Category { get; set; } = string.Empty;
public bool StatusFlag { get; set; }
}

View File

@@ -1,5 +1,5 @@
using DbFirst.BlazorWebApp.Models;
using DbFirst.BlazorWebApp.Models.Grid;
using DbFirst.BlazorWebApp.Models.Grid;
using DbFirst.Contracts.Layouts;
using Microsoft.JSInterop;
using System.Text.Json;

View File

@@ -1,4 +1,5 @@
using DbFirst.BlazorWebApp.Models;
using DbFirst.Contracts.Catalogs;
namespace DbFirst.BlazorWebApp.Services;

View File

@@ -1,4 +1,4 @@
using DbFirst.BlazorWebApp.Models;
using DbFirst.Contracts.Dashboards;
namespace DbFirst.BlazorWebApp.Services;

View File

@@ -1,4 +1,5 @@
using DbFirst.BlazorWebApp.Models;
using DbFirst.Contracts.Catalogs;
namespace DbFirst.BlazorWebApp.Services;

View File

@@ -1,4 +1,4 @@
using DbFirst.BlazorWebApp.Models;
using DbFirst.Contracts.Dashboards;
namespace DbFirst.BlazorWebApp.Services
{

View File

@@ -1,4 +1,4 @@
using DbFirst.BlazorWebApp.Models;
using DbFirst.Contracts.Layouts;
namespace DbFirst.BlazorWebApp.Services
{

View File

@@ -1,4 +1,5 @@
using DbFirst.BlazorWebApp.Models;
using DbFirst.Contracts.MassData;
namespace DbFirst.BlazorWebApp.Services
{

View File

@@ -1,4 +1,4 @@
using DbFirst.BlazorWebApp.Models;
using DbFirst.Contracts.Layouts;
namespace DbFirst.BlazorWebApp.Services;

View File

@@ -1,4 +1,5 @@
using DbFirst.BlazorWebApp.Models;
using DbFirst.Contracts.MassData;
namespace DbFirst.BlazorWebApp.Services;

View File

@@ -12,24 +12,54 @@ public class ThemeState
}
public bool IsDarkMode { get; private set; }
public string CurrentThemeName { get; private set; } = "Fluent";
/// <summary>
/// Themes die eine native DevExpress Dark-Variante besitzen:
/// - Fluent ? Themes.Fluent.Clone(ThemeMode.Dark), verwendet --DS-* Token-System
/// - BlazingBerry ? Themes.BlazingDark
/// Alle anderen Themes (Purple, OfficeWhite, BootstrapExternal) haben keine offizielle
/// Dark-Variante; dort übernehmen CSS-Overrides auf --dxbl-grid-* Variablen die Arbeit.
/// </summary>
public bool IsNativeDarkTheme => IsDarkMode &&
(CurrentThemeName == "Fluent" || CurrentThemeName == "BlazingBerry");
public static readonly List<string> AvailableThemes = ["Fluent", "BlazingBerry", "Purple", "OfficeWhite", "BootstrapExternal"];
public event Action? OnChange;
public void SetDarkMode(bool isDarkMode)
public void SetTheme(string themeName)
{
if (IsDarkMode == isDarkMode)
{
return;
}
IsDarkMode = isDarkMode;
var theme = Themes.Fluent.Clone(properties =>
{
properties.Mode = isDarkMode ? ThemeMode.Dark : ThemeMode.Light;
properties.ApplyToPageElements = true;
});
themeChangeService.SetTheme(theme);
if (CurrentThemeName == themeName) return;
CurrentThemeName = themeName;
ApplyTheme();
OnChange?.Invoke();
}
}
public void SetDarkMode(bool isDarkMode)
{
if (IsDarkMode == isDarkMode) return;
IsDarkMode = isDarkMode;
ApplyTheme();
OnChange?.Invoke();
}
private void ApplyTheme()
{
if (CurrentThemeName == "Fluent")
{
var theme = Themes.Fluent.Clone(properties =>
{
properties.Mode = IsDarkMode ? ThemeMode.Dark : ThemeMode.Light;
properties.ApplyToPageElements = true;
});
themeChangeService.SetTheme(theme);
}
else if (CurrentThemeName == "BlazingBerry") themeChangeService.SetTheme(IsDarkMode ? Themes.BlazingDark : Themes.BlazingBerry);
else if (CurrentThemeName == "Purple") themeChangeService.SetTheme(Themes.Purple);
else if (CurrentThemeName == "OfficeWhite") themeChangeService.SetTheme(Themes.OfficeWhite);
else if (CurrentThemeName == "BootstrapExternal") themeChangeService.SetTheme(Themes.BootstrapExternal);
else
themeChangeService.SetTheme(Themes.Fluent);
}
}

View File

@@ -5,5 +5,11 @@
"Microsoft.AspNetCore": "Warning"
}
},
"ApiBaseUrl": "https://localhost:7204/"
"ApiBaseUrl": "https://localhost:7204/",
"BrowserLink": {
"Enabled": false
},
"DetailedErrors": true
}

View File

@@ -148,6 +148,105 @@ dxbl-grid tbody tr:nth-child(even) td {
background-color: var(--grid-stripe-bg) !important;
}
/* ?? Dark-Mode-Overrides für nicht-native Themes ?????????????????????????????
Strategie: CSS-Custom-Properties werden von DevExpress DIREKT auf den
Komponenten-Elementen definiert (z. B. --dxbl-popup-bg:#fff auf .dxbl-modal).
Eine geerbte Variable aus html.dx-dark würde durch die direkte Zuweisung
überschrieben. Deshalb targeten wir exakt dieselben Elemente, aber mit einem
zusätzlichen Vorfahren-Selektor (html.dx-dark) für höhere Spezifizität:
html.dx-dark .dxbl-modal = (0,2,1) > .dxbl-modal = (0,1,0) ?
html.dx-dark wird per JS gesetzt, wenn IsDarkMode && !IsNativeDarkTheme.
?? */
/* Popup / Modal (CRUD-Dialoge) Variablen-Quelle: .dxbl-modal */
html.dx-dark .dxbl-modal {
--dxbl-popup-bg: #2d2d2d;
--dxbl-popup-color: #e8e8e8;
--dxbl-popup-border-color: #555;
--dxbl-popup-header-bg: #333;
--dxbl-popup-header-color: #e8e8e8;
--dxbl-popup-footer-bg: #333;
--dxbl-popup-footer-color: #e8e8e8;
}
/* Flyout (Column Chooser, Filter-Panel) Variablen-Quelle: .dxbl-flyout */
html.dx-dark .dxbl-flyout {
--dxbl-flyout-bg: #2d2d2d;
--dxbl-flyout-color: #e8e8e8;
--dxbl-flyout-border-color: #555;
--dxbl-flyout-header-bg: #333;
--dxbl-flyout-header-color: #e8e8e8;
--dxbl-flyout-footer-bg: #333;
}
/* Dropdown (ComboBox-Klappliste, Band-Dropdowns) Quelle: .dxbl-dropdown */
html.dx-dark .dxbl-dropdown,
html.dx-dark .dxbl-itemlist-dropdown {
--dxbl-dropdown-bg: #2d2d2d;
--dxbl-dropdown-color: #e8e8e8;
--dxbl-dropdown-border-color: #555;
--dxbl-dropdown-header-bg: #333;
--dxbl-dropdown-footer-bg: #333;
}
/* Edit-Dropdown (ComboBox-Popup wenn als Modal gerendert) Quelle: .dxbl-edit-dropdown */
html.dx-dark .dxbl-edit-dropdown {
--dxbl-edit-dropdown-bg: #2d2d2d;
--dxbl-edit-dropdown-color: #e8e8e8;
--dxbl-edit-dropdown-border-color: #555;
}
/* ListBox (Einträge in Dropdowns) Quelle: .dxbl-list-box */
html.dx-dark .dxbl-list-box,
html.dx-dark .dxbl-list-box-render-container {
--dxbl-list-box-bg: #2d2d2d;
--dxbl-list-box-color: #e8e8e8;
--dxbl-list-box-border-color: #555;
--dxbl-list-box-item-hover-bg: #3a3a3a;
--dxbl-list-box-item-hover-color: #e8e8e8;
}
/* TextEdit / ComboBox Eingabefeld Quelle: .dxbl-text-edit */
html.dx-dark .dxbl-text-edit {
--dxbl-text-edit-bg: #2d2d2d;
--dxbl-text-edit-color: #e8e8e8;
--dxbl-text-edit-border-color: #555;
--dxbl-text-edit-btn-bg: #3a3a3a;
--dxbl-text-edit-btn-color: #e8e8e8;
--dxbl-text-edit-btn-hover-bg: #444;
--dxbl-text-edit-btn-hover-color: #e8e8e8;
}
/* Buttons */
html.dx-dark .dxbl-btn {
--dxbl-btn-color: #e8e8e8;
--dxbl-btn-bg: #3a3a3a;
--dxbl-btn-border-color: #555;
--dxbl-btn-hover-bg: #444;
--dxbl-btn-hover-color: #e8e8e8;
--dxbl-btn-hover-border-color: #666;
}
/* FormLayout */
html.dx-dark .dxbl-fl {
--dxbl-fl-caption-color: #bbb;
--dxbl-fl-group-bg: #242424;
--dxbl-fl-group-color: #e8e8e8;
}
/* Grid */
html.dx-dark .dxbl-grid {
background-color: #242424;
color: #e8e8e8;
border-color: #444;
}
html.dx-dark .dxbl-grid > .dxbl-scroll-viewer,
html.dx-dark .dxbl-grid > .dxbl-grid-top-panel {
background-color: #242424;
color: #e8e8e8;
}
/* MassData-spezifisch */
.page-size-selector {
display: flex;
@@ -185,3 +284,7 @@ dxbl-grid tbody tr:nth-child(even) td {
align-items: center;
justify-content: center;
}
.top-row .btn-gap {
margin-left: 8px;
}

View File

@@ -1,3 +1,4 @@
window.setSize = function (fontSize) {
document.documentElement.style.setProperty('--global-size', fontSize);
};

View File

@@ -1,4 +1,4 @@
namespace DbFirst.Application.Catalogs;
namespace DbFirst.Contracts.Catalogs;
public class CatalogReadDto
{
@@ -9,4 +9,4 @@ public class CatalogReadDto
public DateTime AddedWhen { get; set; }
public string? ChangedWho { get; set; }
public DateTime? ChangedWhen { get; set; }
}
}

View File

@@ -1,10 +1,10 @@
using DbFirst.Domain;
using DbFirst.Domain;
namespace DbFirst.Application.Catalogs;
namespace DbFirst.Contracts.Catalogs;
public class CatalogWriteDto
{
public string CatTitle { get; set; } = string.Empty;
public string CatString { get; set; } = string.Empty;
public CatalogUpdateProcedure UpdateProcedure { get; set; } = CatalogUpdateProcedure.Update;
}
}

View File

@@ -1,7 +1,7 @@
namespace DbFirst.BlazorWebApp.Models;
namespace DbFirst.Contracts.Dashboards;
public class DashboardInfoDto
{
public string Id { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
}
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\DbFirst.Domain\DbFirst.Domain.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,4 +1,4 @@
namespace DbFirst.BlazorWebApp.Models;
namespace DbFirst.Contracts.Layouts;
public class LayoutDto
{
@@ -6,4 +6,4 @@ public class LayoutDto
public string LayoutKey { get; set; } = string.Empty;
public string UserName { get; set; } = string.Empty;
public string LayoutData { get; set; } = string.Empty;
}
}

View File

@@ -1,4 +1,4 @@
namespace DbFirst.BlazorWebApp.Models;
namespace DbFirst.Contracts.MassData;
public class MassDataReadDto
{
@@ -9,4 +9,4 @@ public class MassDataReadDto
public bool StatusFlag { get; set; }
public DateTime AddedWhen { get; set; }
public DateTime? ChangedWhen { get; set; }
}
}

View File

@@ -1,4 +1,4 @@
namespace DbFirst.Application.MassData;
namespace DbFirst.Contracts.MassData;
public class MassDataWriteDto
{
@@ -6,4 +6,4 @@ public class MassDataWriteDto
public decimal Amount { get; set; }
public string Category { get; set; } = string.Empty;
public bool StatusFlag { get; set; }
}
}

View File

@@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="AutoMapper" Version="16.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.22" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.22" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.22">

View File

@@ -1,6 +1,6 @@
using DbFirst.Application.Repositories;
using DbFirst.Domain;
using DbFirst.Domain.Entities;
using DbFirst.Application.Repositories;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using System.Data;

View File

@@ -1,8 +1,8 @@
using System.Data;
using DbFirst.Application.Repositories;
using DbFirst.Domain.Entities;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using System.Data;
namespace DbFirst.Infrastructure.Repositories;

View File

@@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DbFirst.Domain", "DbFirst.D
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DbFirst.BlazorWebApp", "DbFirst.BlazorWebApp\DbFirst.BlazorWebApp.csproj", "{FF1D77C4-A13D-43E0-BCE1-C18C01F1767C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DbFirst.Contracts", "DbFirst.Contracts\DbFirst.Contracts.csproj", "{94FFCA01-9476-49B3-B7D0-5706514E42E4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -83,6 +85,18 @@ Global
{FF1D77C4-A13D-43E0-BCE1-C18C01F1767C}.Release|x64.Build.0 = Release|Any CPU
{FF1D77C4-A13D-43E0-BCE1-C18C01F1767C}.Release|x86.ActiveCfg = Release|Any CPU
{FF1D77C4-A13D-43E0-BCE1-C18C01F1767C}.Release|x86.Build.0 = Release|Any CPU
{94FFCA01-9476-49B3-B7D0-5706514E42E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{94FFCA01-9476-49B3-B7D0-5706514E42E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{94FFCA01-9476-49B3-B7D0-5706514E42E4}.Debug|x64.ActiveCfg = Debug|Any CPU
{94FFCA01-9476-49B3-B7D0-5706514E42E4}.Debug|x64.Build.0 = Debug|Any CPU
{94FFCA01-9476-49B3-B7D0-5706514E42E4}.Debug|x86.ActiveCfg = Debug|Any CPU
{94FFCA01-9476-49B3-B7D0-5706514E42E4}.Debug|x86.Build.0 = Debug|Any CPU
{94FFCA01-9476-49B3-B7D0-5706514E42E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{94FFCA01-9476-49B3-B7D0-5706514E42E4}.Release|Any CPU.Build.0 = Release|Any CPU
{94FFCA01-9476-49B3-B7D0-5706514E42E4}.Release|x64.ActiveCfg = Release|Any CPU
{94FFCA01-9476-49B3-B7D0-5706514E42E4}.Release|x64.Build.0 = Release|Any CPU
{94FFCA01-9476-49B3-B7D0-5706514E42E4}.Release|x86.ActiveCfg = Release|Any CPU
{94FFCA01-9476-49B3-B7D0-5706514E42E4}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE