Normalize dashboard date dimensions, update grid UI
Added normalization for date dimensions in SqlDashboardStorage to ensure consistent grouping for "AddedWhen" and "ChangedWhen" fields. Refactored CatalogsGrid.razor to use custom sort icons and default DxGrid filter row UI, simplifying markup and improving visual consistency. Updated related CSS for sortable headers and filter inputs.
This commit is contained in:
@@ -52,7 +52,33 @@ public sealed class SqlDashboardStorage : IEditableDashboardStorage
|
|||||||
}
|
}
|
||||||
|
|
||||||
var xml = Encoding.UTF8.GetString(data);
|
var xml = Encoding.UTF8.GetString(data);
|
||||||
return XDocument.Parse(xml);
|
var doc = XDocument.Parse(xml);
|
||||||
|
NormalizeCatalogDateDimensions(doc);
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void NormalizeCatalogDateDimensions(XDocument doc)
|
||||||
|
{
|
||||||
|
var dateMembers = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
"AddedWhen",
|
||||||
|
"ChangedWhen"
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var dimension in doc.Descendants("Dimension"))
|
||||||
|
{
|
||||||
|
var member = dimension.Attribute("DataMember")?.Value;
|
||||||
|
if (member == null || !dateMembers.Contains(member))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var interval = dimension.Attribute("DateTimeGroupInterval")?.Value;
|
||||||
|
if (string.IsNullOrWhiteSpace(interval) || string.Equals(interval, "Year", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
dimension.SetAttributeValue("DateTimeGroupInterval", "DayMonthYear");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string AddDashboard(XDocument dashboard, string dashboardName)
|
public string AddDashboard(XDocument dashboard, string dashboardName)
|
||||||
|
|||||||
@@ -3,12 +3,16 @@
|
|||||||
<style>
|
<style>
|
||||||
.action-panel { margin-bottom: 16px; }
|
.action-panel { margin-bottom: 16px; }
|
||||||
.grid-section { margin-top: 12px; }
|
.grid-section { margin-top: 12px; }
|
||||||
|
.catalog-grid .dxbl-grid-sort-asc,
|
||||||
|
.catalog-grid .dxbl-grid-sort-desc {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
.catalog-grid th.dxbl-grid-header-sortable {
|
.catalog-grid th.dxbl-grid-header-sortable {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-right: 1.5rem;
|
padding-right: 1.5rem;
|
||||||
}
|
}
|
||||||
.catalog-grid th.dxbl-grid-header-sortable:not(:has(.dxbl-grid-sort-asc)):not(:has(.dxbl-grid-sort-desc))::before,
|
.catalog-grid th.dxbl-grid-header-sortable::before,
|
||||||
.catalog-grid th.dxbl-grid-header-sortable:not(:has(.dxbl-grid-sort-asc)):not(:has(.dxbl-grid-sort-desc))::after {
|
.catalog-grid th.dxbl-grid-header-sortable::after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0.45rem;
|
right: 0.45rem;
|
||||||
@@ -19,14 +23,20 @@
|
|||||||
opacity: 0.35;
|
opacity: 0.35;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
.catalog-grid th.dxbl-grid-header-sortable:not(:has(.dxbl-grid-sort-asc)):not(:has(.dxbl-grid-sort-desc))::before {
|
.catalog-grid th.dxbl-grid-header-sortable::before {
|
||||||
top: 38%;
|
top: 38%;
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.957 10.999a1 1 0 0 1-.821-1.571l2.633-3.785a1.5 1.5 0 0 1 2.462 0l2.633 3.785a1 1 0 0 1-.821 1.57H4.957Z' fill='%23888888'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.957 10.999a1 1 0 0 1-.821-1.571l2.633-3.785a1.5 1.5 0 0 1 2.462 0l2.633 3.785a1 1 0 0 1-.821 1.57H4.957Z' fill='%23888888'/%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
.catalog-grid th.dxbl-grid-header-sortable:not(:has(.dxbl-grid-sort-asc)):not(:has(.dxbl-grid-sort-desc))::after {
|
.catalog-grid th.dxbl-grid-header-sortable::after {
|
||||||
top: 58%;
|
top: 58%;
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.957 5a1 1 0 0 0-.821 1.571l2.633 3.784a1.5 1.5 0 0 0 2.462 0l2.633-3.784A1 1 0 0 0 11.043 5H4.957Z' fill='%23888888'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.957 5a1 1 0 0 0-.821 1.571l2.633 3.784a1.5 1.5 0 0 0 2.462 0l2.633-3.784A1 1 0 0 0 11.043 5H4.957Z' fill='%23888888'/%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
.catalog-grid th.dxbl-grid-header-sortable[aria-sort="ascending"]::after {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.catalog-grid th.dxbl-grid-header-sortable[aria-sort="descending"]::before {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
.catalog-grid .filter-search-input input {
|
.catalog-grid .filter-search-input input {
|
||||||
padding-right: 1.75rem;
|
padding-right: 1.75rem;
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M9.309 10.016a4.5 4.5 0 1 1 .707-.707l3.838 3.837a.5.5 0 0 1-.708.708L9.31 10.016ZM10 6.5a3.5 3.5 0 1 0-7 0 3.5 3.5 0 0 0 7 0Z' fill='%23666666'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M9.309 10.016a4.5 4.5 0 1 1 .707-.707l3.838 3.837a.5.5 0 0 1-.708.708L9.31 10.016ZM10 6.5a3.5 3.5 0 1 0-7 0 3.5 3.5 0 0 0 7 0Z' fill='%23666666'/%3E%3C/svg%3E");
|
||||||
|
|||||||
@@ -3,37 +3,6 @@
|
|||||||
<style>
|
<style>
|
||||||
.action-panel { margin-bottom: 16px; }
|
.action-panel { margin-bottom: 16px; }
|
||||||
.grid-section { margin-top: 12px; }
|
.grid-section { margin-top: 12px; }
|
||||||
.catalog-grid th.dxbl-grid-header-sortable {
|
|
||||||
position: relative;
|
|
||||||
padding-right: 1.5rem;
|
|
||||||
}
|
|
||||||
.catalog-grid th.dxbl-grid-header-sortable:not(:has(.dxbl-grid-sort-asc)):not(:has(.dxbl-grid-sort-desc))::before,
|
|
||||||
.catalog-grid th.dxbl-grid-header-sortable:not(:has(.dxbl-grid-sort-asc)):not(:has(.dxbl-grid-sort-desc))::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
right: 0.45rem;
|
|
||||||
width: 0.7rem;
|
|
||||||
height: 0.7rem;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 0.7rem 0.7rem;
|
|
||||||
opacity: 0.35;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
.catalog-grid th.dxbl-grid-header-sortable:not(:has(.dxbl-grid-sort-asc)):not(:has(.dxbl-grid-sort-desc))::before {
|
|
||||||
top: 38%;
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.957 10.999a1 1 0 0 1-.821-1.571l2.633-3.785a1.5 1.5 0 0 1 2.462 0l2.633 3.785a1 1 0 0 1-.821 1.57H4.957Z' fill='%23888888'/%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
.catalog-grid th.dxbl-grid-header-sortable:not(:has(.dxbl-grid-sort-asc)):not(:has(.dxbl-grid-sort-desc))::after {
|
|
||||||
top: 58%;
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.957 5a1 1 0 0 0-.821 1.571l2.633 3.784a1.5 1.5 0 0 0 2.462 0l2.633-3.784A1 1 0 0 0 11.043 5H4.957Z' fill='%23888888'/%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
.catalog-grid .filter-search-input input {
|
|
||||||
padding-right: 1.75rem;
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M9.309 10.016a4.5 4.5 0 1 1 .707-.707l3.838 3.837a.5.5 0 0 1-.708.708L9.31 10.016ZM10 6.5a3.5 3.5 0 1 0-7 0 3.5 3.5 0 0 0 7 0Z' fill='%23666666'/%3E%3C/svg%3E");
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: right 0.5rem center;
|
|
||||||
background-size: 0.9rem;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
||||||
@@ -93,42 +62,12 @@ else
|
|||||||
<div class="grid-section">
|
<div class="grid-section">
|
||||||
<DxGrid Data="@items" TItem="CatalogReadDto" KeyFieldName="@nameof(CatalogReadDto.Guid)" ShowFilterRow="true" PageSize="10" CssClass="mb-4 catalog-grid">
|
<DxGrid Data="@items" TItem="CatalogReadDto" KeyFieldName="@nameof(CatalogReadDto.Guid)" ShowFilterRow="true" PageSize="10" CssClass="mb-4 catalog-grid">
|
||||||
<Columns>
|
<Columns>
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.Guid)" Caption="Id" Width="140px" SortIndex="0" SortOrder="GridColumnSortOrder.Ascending">
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.Guid)" Caption="Id" Width="140px" SortIndex="0" SortOrder="GridColumnSortOrder.Ascending" />
|
||||||
<FilterRowCellTemplate Context="filter">
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.CatTitle)" Caption="Titel" />
|
||||||
<DxTextBox Text="@(filter.FilterRowValue?.ToString())"
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.CatString)" Caption="String" />
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.AddedWho)" Caption="Angelegt von" />
|
||||||
CssClass="filter-search-input" />
|
|
||||||
</FilterRowCellTemplate>
|
|
||||||
</DxGridDataColumn>
|
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.CatTitle)" Caption="Titel">
|
|
||||||
<FilterRowCellTemplate Context="filter">
|
|
||||||
<DxTextBox Text="@(filter.FilterRowValue as string)"
|
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
|
||||||
CssClass="filter-search-input" />
|
|
||||||
</FilterRowCellTemplate>
|
|
||||||
</DxGridDataColumn>
|
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.CatString)" Caption="String">
|
|
||||||
<FilterRowCellTemplate Context="filter">
|
|
||||||
<DxTextBox Text="@(filter.FilterRowValue as string)"
|
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
|
||||||
CssClass="filter-search-input" />
|
|
||||||
</FilterRowCellTemplate>
|
|
||||||
</DxGridDataColumn>
|
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.AddedWho)" Caption="Angelegt von">
|
|
||||||
<FilterRowCellTemplate Context="filter">
|
|
||||||
<DxTextBox Text="@(filter.FilterRowValue as string)"
|
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
|
||||||
CssClass="filter-search-input" />
|
|
||||||
</FilterRowCellTemplate>
|
|
||||||
</DxGridDataColumn>
|
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.AddedWhen)" Caption="Angelegt am" />
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.AddedWhen)" Caption="Angelegt am" />
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.ChangedWho)" Caption="Geändert von">
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.ChangedWho)" Caption="Geändert von" />
|
||||||
<FilterRowCellTemplate Context="filter">
|
|
||||||
<DxTextBox Text="@(filter.FilterRowValue as string)"
|
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
|
||||||
CssClass="filter-search-input" />
|
|
||||||
</FilterRowCellTemplate>
|
|
||||||
</DxGridDataColumn>
|
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.ChangedWhen)" Caption="Geändert am" />
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.ChangedWhen)" Caption="Geändert am" />
|
||||||
<DxGridDataColumn Caption="" Width="220px" AllowSort="false">
|
<DxGridDataColumn Caption="" Width="220px" AllowSort="false">
|
||||||
<CellDisplayTemplate Context="cell">
|
<CellDisplayTemplate Context="cell">
|
||||||
|
|||||||
Reference in New Issue
Block a user