diff --git a/DbViewer.Cql/CqlPropertyDescriptionBuilder.cs b/DbViewer.Cql/CqlPropertyDescriptionBuilder.cs index 9d437112..08ff2f8c 100644 --- a/DbViewer.Cql/CqlPropertyDescriptionBuilder.cs +++ b/DbViewer.Cql/CqlPropertyDescriptionBuilder.cs @@ -3,6 +3,7 @@ using System.Reflection; using Cassandra; +using Cassandra.Mapping; using Cassandra.Mapping.Attributes; using SkbKontur.DbViewer.Configuration; @@ -12,35 +13,107 @@ namespace SkbKontur.DbViewer.Cql { public class CqlPropertyDescriptionBuilder : IPropertyDescriptionBuilder { + private string PrettyPrintPropertyCqlAttributes(PropertyInfo propertyInfo) + { + var columnMeta = $"Тип: {propertyInfo.PropertyType.Name}\n"; + + var columnAttribute = propertyInfo.GetCustomAttribute(); + if (columnAttribute != null) + { + var type = columnAttribute.Type == null ? string.Empty : $", Type = typeof({columnAttribute.Type.Name})"; + columnMeta += $"[Column(\"{columnAttribute.Name}\"{type})]\n"; + } + + var partitionKeyAttribute = propertyInfo.GetCustomAttribute(); + if (partitionKeyAttribute != null) + { + columnMeta += $"[PartitionKey({partitionKeyAttribute.Index})]"; + } + + var clusteringKeyAttribute = propertyInfo.GetCustomAttribute(); + if (clusteringKeyAttribute != null) + { + var sort = clusteringKeyAttribute.ClusteringSortOrder == SortOrder.Unspecified ? null : $", SortOrder.{clusteringKeyAttribute.ClusteringSortOrder}"; + var name = string.IsNullOrEmpty(clusteringKeyAttribute.Name) ? null : $", Name = \"{clusteringKeyAttribute.Name}\""; + columnMeta += $"[ClusteringKey({clusteringKeyAttribute.Index}{sort}{name})]"; + } + + return columnMeta; + } + public PropertyMetaInformation Build(PropertyInfo propertyInfo, Type typeInfo) { var result = new PropertyMetaInformation { Name = propertyInfo.Name, + Meta = PrettyPrintPropertyCqlAttributes(propertyInfo), + RequiredForFilter = Array.Empty(), + RequiredForSort = new SortRequirements + { + RequiredFilters = Array.Empty(), + RequiredSorts = Array.Empty(), + }, }; - if (propertyInfo.CustomAttributes.Any(x => x.AttributeType == typeof(PartitionKeyAttribute))) + var partitionKeys = propertyInfo.ReflectedType + .GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Select(x => (Property : x, Attribute : x.GetCustomAttribute())) + .Where(x => x.Attribute != null) + .OrderBy(x => x.Attribute.Index) + .ToArray(); + var partitionKeyAttribute = propertyInfo.GetCustomAttribute(); + if (partitionKeyAttribute != null) { result.IsSearchable = true; result.IsIdentity = true; result.IsRequired = true; - result.AvailableFilters = new[] {ObjectFieldFilterOperator.Equals}; + result.AvailableFilters = equals; } - if (propertyInfo.CustomAttributes.Any(x => x.AttributeType == typeof(ClusteringKeyAttribute))) + var clusteringKeys = propertyInfo.ReflectedType + .GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Select(x => (Property : x, Attribute : x.GetCustomAttribute())) + .Where(x => x.Attribute != null) + .OrderBy(x => x.Attribute.Index) + .ToArray(); + var clusteringKeyAttribute = propertyInfo.GetCustomAttribute(); + if (clusteringKeyAttribute != null) { result.IsSearchable = true; result.IsIdentity = true; result.IsSortable = true; - result.AvailableFilters = specialTypes.Contains(propertyInfo.PropertyType) - ? new[] {ObjectFieldFilterOperator.Equals} - : defaultFilters; + result.AvailableFilters = specialTypes.Contains(propertyInfo.PropertyType) ? equals : defaultFilters; + + result.RequiredForFilter = + partitionKeys + .Select(x => new FilterRequirement {AvailableFilters = equals, PropertyName = x.Property.Name}) + .Concat( + clusteringKeys + .Where(x => x.Attribute.Index < clusteringKeyAttribute.Index) + .Select(x => new FilterRequirement {AvailableFilters = equals, PropertyName = x.Property.Name}) + ) + .ToArray(); + + result.RequiredForSort = new SortRequirements + { + RequiredFilters = + partitionKeys + .Select(x => new FilterRequirement {AvailableFilters = equals, PropertyName = x.Property.Name}) + .ToArray(), + OneDirectionSort = true, + RequiredSorts = + clusteringKeys + .Where(x => x.Attribute.Index < clusteringKeyAttribute.Index) + .Select(x => x.Property.Name) + .ToArray(), + }; } return result; } private static readonly Type[] specialTypes = {typeof(TimeUuid), typeof(bool)}; + private static readonly ObjectFieldFilterOperator[] equals = {ObjectFieldFilterOperator.Equals}; private static readonly ObjectFieldFilterOperator[] defaultFilters = { diff --git a/DbViewer.EntityFramework/EntityFrameworkPropertyDescriptionBuilder.cs b/DbViewer.EntityFramework/EntityFrameworkPropertyDescriptionBuilder.cs index 8718817c..6574adfa 100644 --- a/DbViewer.EntityFramework/EntityFrameworkPropertyDescriptionBuilder.cs +++ b/DbViewer.EntityFramework/EntityFrameworkPropertyDescriptionBuilder.cs @@ -16,6 +16,12 @@ public PropertyMetaInformation Build(PropertyInfo propertyInfo, Type typeInfo) var result = new PropertyMetaInformation { Name = propertyInfo.Name, + RequiredForFilter = Array.Empty(), + RequiredForSort = new SortRequirements + { + RequiredFilters = Array.Empty(), + RequiredSorts = Array.Empty(), + }, }; if (propertyInfo.CustomAttributes.Any(x => x.AttributeType == typeof(TPrimaryKey))) diff --git a/DbViewer.TestApi/Impl/SamplePropertyDescriptionBuilder.cs b/DbViewer.TestApi/Impl/SamplePropertyDescriptionBuilder.cs index 607595c9..1547dafc 100644 --- a/DbViewer.TestApi/Impl/SamplePropertyDescriptionBuilder.cs +++ b/DbViewer.TestApi/Impl/SamplePropertyDescriptionBuilder.cs @@ -16,6 +16,12 @@ public PropertyMetaInformation Build(PropertyInfo propertyInfo, Type typeInfo) var result = new PropertyMetaInformation { Name = propertyInfo.Name, + RequiredForFilter = Array.Empty(), + RequiredForSort = new SortRequirements + { + RequiredFilters = Array.Empty(), + RequiredSorts = Array.Empty(), + } }; var indexed = propertyInfo.GetCustomAttribute(typeof(IndexedAttribute)) != null; var required = propertyInfo.GetCustomAttribute(typeof(RequiredAttribute)) != null; diff --git a/DbViewer/DataTypes/FilterRequirement.cs b/DbViewer/DataTypes/FilterRequirement.cs new file mode 100644 index 00000000..196997c2 --- /dev/null +++ b/DbViewer/DataTypes/FilterRequirement.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace SkbKontur.DbViewer.DataTypes +{ + public class FilterRequirement + { + [JsonProperty("availableFilters")] + public ObjectFieldFilterOperator[] AvailableFilters { get; set; } + + [JsonProperty("propertyName")] + public string PropertyName { get; set; } + } +} \ No newline at end of file diff --git a/DbViewer/DataTypes/PropertyMetaInformation.cs b/DbViewer/DataTypes/PropertyMetaInformation.cs index bc6888c2..27e66317 100644 --- a/DbViewer/DataTypes/PropertyMetaInformation.cs +++ b/DbViewer/DataTypes/PropertyMetaInformation.cs @@ -28,6 +28,15 @@ public class PropertyMetaInformation [JsonProperty("availableValues")] public string[] AvailableValues { get; set; } + [JsonProperty("requiredForFilter")] + public FilterRequirement[] RequiredForFilter { get; set; } + + [JsonProperty("requiredForSort")] + public SortRequirements RequiredForSort { get; set; } + + [JsonProperty("meta")] + public string? Meta { get; set; } + [JsonProperty("type")] public TypeMetaInformation Type { get; set; } } diff --git a/DbViewer/DataTypes/SortRequirements.cs b/DbViewer/DataTypes/SortRequirements.cs new file mode 100644 index 00000000..50bc9483 --- /dev/null +++ b/DbViewer/DataTypes/SortRequirements.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace SkbKontur.DbViewer.DataTypes +{ + public class SortRequirements + { + [JsonProperty("requiredFilters")] + public FilterRequirement[] RequiredFilters { get; set; } + + [JsonProperty("oneDirectionSort")] + public bool OneDirectionSort { get; set; } + + [JsonProperty("requiredSorts")] + public string[] RequiredSorts { get; set; } + } +} \ No newline at end of file diff --git a/DbViewer/Helpers/PropertyHelpers.cs b/DbViewer/Helpers/PropertyHelpers.cs index a7f070ea..94602c19 100644 --- a/DbViewer/Helpers/PropertyHelpers.cs +++ b/DbViewer/Helpers/PropertyHelpers.cs @@ -142,12 +142,19 @@ private static PropertyMetaInformation BuildPropertyInfo(object? @object, Proper { Name = propertyInfo.Name, AvailableFilters = propertyDescription.AvailableFilters, - AvailableValues = underlyingType.IsEnum ? Enum.GetNames(underlyingType) : new string[0], + AvailableValues = underlyingType.IsEnum ? Enum.GetNames(underlyingType) : Array.Empty(), IsEditable = propertyInfo.SetMethod != null, IsIdentity = propertyDescription.IsIdentity, IsRequired = propertyDescription.IsRequired, IsSearchable = propertyDescription.IsSearchable, IsSortable = propertyDescription.IsSortable, + RequiredForFilter = propertyDescription.RequiredForFilter ?? Array.Empty(), + RequiredForSort = propertyDescription.RequiredForSort ?? new SortRequirements + { + RequiredFilters = Array.Empty(), + RequiredSorts = Array.Empty(), + }, + Meta = propertyDescription.Meta, Type = BuildTypeMetaInformation(objectProperty, propertyType, originalPropertyType, propertyDescriptionBuilder, @object == null ? null : propertyConfigurationProvider, types), }; } diff --git a/db-viewer-ui/src/Components/FormRow/FormRow.tsx b/db-viewer-ui/src/Components/FormRow/FormRow.tsx index e202c717..47380557 100644 --- a/db-viewer-ui/src/Components/FormRow/FormRow.tsx +++ b/db-viewer-ui/src/Components/FormRow/FormRow.tsx @@ -1,5 +1,5 @@ import { Fit, Fixed, RowStack } from "@skbkontur/react-stack-layout"; -import { ThemeContext } from "@skbkontur/react-ui"; +import { Hint, ThemeContext } from "@skbkontur/react-ui"; import React from "react"; import { jsStyles } from "./FormRow.styles"; @@ -8,14 +8,30 @@ export interface FormRowProps { caption?: string | JSX.Element; captionWidth?: number; children?: React.ReactNode; + hint?: null | string; } -export function FormRow({ caption, captionWidth, children }: FormRowProps) { +export function FormRow({ caption, captionWidth, hint, children }: FormRowProps): JSX.Element { const theme = React.useContext(ThemeContext); + + const hintElement = hint ? ( +
+ {hint.split("\n").map(x => ( +
{x}
+ ))} +
+ ) : null; + return ( - {caption} + {hintElement ? ( + + {caption} + + ) : ( + caption + )} {children} diff --git a/db-viewer-ui/src/Components/ObjectFilter/ObjectFilter.tsx b/db-viewer-ui/src/Components/ObjectFilter/ObjectFilter.tsx index 1ddb6ea1..6bbef4ba 100644 --- a/db-viewer-ui/src/Components/ObjectFilter/ObjectFilter.tsx +++ b/db-viewer-ui/src/Components/ObjectFilter/ObjectFilter.tsx @@ -1,5 +1,6 @@ +import HelpDotIcon from "@skbkontur/react-icons/HelpDot"; import { ColumnStack, Fit, RowStack } from "@skbkontur/react-stack-layout"; -import { Input } from "@skbkontur/react-ui"; +import { Input, Link, Tooltip } from "@skbkontur/react-ui"; import { tooltip, ValidationInfo, ValidationWrapper } from "@skbkontur/react-ui-validations"; import React from "react"; @@ -12,6 +13,7 @@ import { TimeUtils } from "../../Domain/Utils/TimeUtils"; import { validateObjectField } from "../../Domain/Utils/ValidationUtils"; import { DateTimePicker } from "../DateTimeRangePicker/DateTimePicker"; import { FormRow } from "../FormRow/FormRow"; +import { getMissingFilters, MissingFilters } from "../ObjectTable/TooltipContent"; import { OperatorSelect, StyledSelect } from "./OperatorSelect"; @@ -35,8 +37,41 @@ export class ObjectFilter extends React.Component { return result; } - public updateItem(property: PropertyMetaInformation, conditionUpdate: Partial) { - const { conditions, onChange } = this.props; + public brokenDependency(condition: Condition, dependent: undefined | PropertyMetaInformation): boolean { + console.info(`brokenDependency(${condition.path}, ${dependent?.name})`); + if (!dependent) { + return false; + } + + const requirement = dependent.requiredForFilter.find(x => x.propertyName === condition.path); + if (!requirement) { + return false; + } + + console.info(StringUtils.isNullOrWhitespace(condition.value)); + console.info(requirement.availableFilters.indexOf(condition.operator) === -1); + return ( + StringUtils.isNullOrWhitespace(condition.value) || + requirement.availableFilters.indexOf(condition.operator) === -1 + ); + } + + public updateItem(property: PropertyMetaInformation, conditionUpdate: Partial): void { + const { onChange, tableColumns } = this.props; + + const thisCondition = { + ...(this.props.conditions.find(x => x.path === property.name) ?? this.getCondition(property)), + ...conditionUpdate, + }; + const dependents = tableColumns.filter(x => x.requiredForFilter.find(y => y.propertyName === property.name)); + console.info(this.props.conditions); + const conditions = this.props.conditions.filter( + x => + !this.brokenDependency( + thisCondition, + dependents.find(d => d.name === x.path) + ) + ); const conditionIndex = conditions.findIndex(x => x.path === property.name); if (conditionIndex >= 0) { onChange([ @@ -49,7 +84,7 @@ export class ObjectFilter extends React.Component { } } - public renderProperty(property: PropertyMetaInformation, value: Nullable): JSX.Element { + public renderProperty(property: PropertyMetaInformation, value: Nullable, disabled: boolean): JSX.Element { const type = property.type.typeName; if (type === "DateTime" || type === "DateTimeOffset") { return ( @@ -62,6 +97,7 @@ export class ObjectFilter extends React.Component { timeZone={TimeUtils.TimeZones.UTC} onChange={date => this.updateItem(property, { value: timestampToTicks(date) })} value={value ? ticksToTimestamp(value) : null} + disabled={disabled} /> @@ -74,6 +110,7 @@ export class ObjectFilter extends React.Component { data-tid={"DateTimeInTicks"} onValueChange={nextValue => this.updateItem(property, { value: nextValue })} value={value ? value : ""} + disabled={disabled} /> @@ -93,6 +130,7 @@ export class ObjectFilter extends React.Component { this.updateItem(property, { value: nextValue }); }} value={value || undefined} + disabled={disabled} /> ); @@ -110,6 +148,7 @@ export class ObjectFilter extends React.Component { this.updateItem(property, { value: nextValue }); }} value={value || undefined} + disabled={disabled} /> ); @@ -123,6 +162,7 @@ export class ObjectFilter extends React.Component { data-tid="Input" onValueChange={nextValue => this.updateItem(property, { value: nextValue })} value={value || ""} + disabled={disabled} /> ); @@ -135,6 +175,46 @@ export class ObjectFilter extends React.Component { return validateObjectField(value); } + public renderRow(property: PropertyMetaInformation, condition: Condition): JSX.Element { + const { conditions } = this.props; + const missingFilters = getMissingFilters(property.requiredForFilter, conditions); + console.info(property.name, missingFilters); + return ( + + + + this.updateItem(property, { operator: value })} + availableValues={property.availableFilters || [ObjectFieldFilterOperator.Equals]} + disabled={missingFilters.length !== 0} + /> + + {this.renderProperty(property, condition.value, missingFilters.length !== 0)} + {missingFilters.length !== 0 && ( + + ( + + )} + pos="right middle"> + } /> + + + )} + + + ); + } + public render(): JSX.Element { const { tableColumns } = this.props; @@ -142,22 +222,7 @@ export class ObjectFilter extends React.Component { {tableColumns .map(x => [x, this.getCondition(x)] as [PropertyMetaInformation, Condition]) - .map(([property, condition]) => ( - - - - this.updateItem(property, { operator: value })} - availableValues={ - property.availableFilters || [ObjectFieldFilterOperator.Equals] - } - /> - - {this.renderProperty(property, condition.value)} - - - ))} + .map(([property, condition]) => this.renderRow(property, condition))} ); } diff --git a/db-viewer-ui/src/Components/ObjectFilter/OperatorSelect.tsx b/db-viewer-ui/src/Components/ObjectFilter/OperatorSelect.tsx index a0fabcae..585a97b1 100644 --- a/db-viewer-ui/src/Components/ObjectFilter/OperatorSelect.tsx +++ b/db-viewer-ui/src/Components/ObjectFilter/OperatorSelect.tsx @@ -7,6 +7,7 @@ interface OperatorSelectProps { value: Nullable; onChange: (x0: ObjectFieldFilterOperator) => void; availableValues: ObjectFieldFilterOperator[]; + disabled: boolean; } function operatorToString(operation: ObjectFieldFilterOperator): string { @@ -26,7 +27,7 @@ function operatorToString(operation: ObjectFieldFilterOperator): string { } export function OperatorSelect(props: OperatorSelectProps): JSX.Element { - const { availableValues, value, onChange } = props; + const { availableValues, value, onChange, disabled } = props; return ( ); } diff --git a/db-viewer-ui/src/Components/ObjectTable/ObjectTable.styles.ts b/db-viewer-ui/src/Components/ObjectTable/ObjectTable.styles.ts index d9ce8073..d1413dd1 100644 --- a/db-viewer-ui/src/Components/ObjectTable/ObjectTable.styles.ts +++ b/db-viewer-ui/src/Components/ObjectTable/ObjectTable.styles.ts @@ -14,6 +14,23 @@ export const jsStyles = { `; }, + indent(): string { + return css` + padding-left: 20px; + `; + }, + + code(): string { + return css` + padding: 2px 4px; + font-size: 90%; + color: rgb(199, 37, 78); + background-color: rgb(249, 242, 244); + border-radius: 4px; + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + `; + }, + headerCell(): string { return css` font: inherit; diff --git a/db-viewer-ui/src/Components/ObjectTable/ObjectTable.tsx b/db-viewer-ui/src/Components/ObjectTable/ObjectTable.tsx index e6d34d1e..a17394d4 100644 --- a/db-viewer-ui/src/Components/ObjectTable/ObjectTable.tsx +++ b/db-viewer-ui/src/Components/ObjectTable/ObjectTable.tsx @@ -1,11 +1,14 @@ +import HelpDotIcon from "@skbkontur/react-icons/HelpDot"; import SortDefaultIcon from "@skbkontur/react-icons/SortDefault"; import SortDownIcon from "@skbkontur/react-icons/SortDown"; import SortUpIcon from "@skbkontur/react-icons/SortUp"; -import { Link, ThemeContext } from "@skbkontur/react-ui"; +import { Tooltip, Link, ThemeContext } from "@skbkontur/react-ui"; import React from "react"; +import { Condition } from "../../Domain/Api/DataTypes/Condition"; import { PropertyMetaInformation } from "../../Domain/Api/DataTypes/PropertyMetaInformation"; import { Sort } from "../../Domain/Api/DataTypes/Sort"; +import { SortRequirements } from "../../Domain/Api/DataTypes/SortRequirements"; import { ICustomRenderer } from "../../Domain/Objects/CustomRenderer"; import { ConfirmDeleteObjectModal } from "../ConfirmDeleteObjectModal/ConfirmDeleteObjectModal"; import { ScrollableContainer } from "../Layouts/ScrollableContainer"; @@ -13,6 +16,7 @@ import { renderForTableCell } from "../ObjectViewer/ObjectItemRender"; import { RouterLink } from "../RouterLink/RouterLink"; import { jsStyles } from "./ObjectTable.styles"; +import { getMissingFilters, MissingFilters, MissingSorts } from "./TooltipContent"; interface ObjectTableProps { customRenderer: ICustomRenderer; @@ -23,6 +27,7 @@ interface ObjectTableProps { onDetailsClick: (item: object) => string; onDeleteClick: (index: number) => Promise; currentSorts: Sort[]; + currentFilters: Condition[]; allowDelete: boolean; allowSort: boolean; } @@ -36,6 +41,7 @@ export function ObjectTable({ onDetailsClick, onDeleteClick, currentSorts, + currentFilters, allowDelete, allowSort, }: ObjectTableProps): JSX.Element { @@ -85,20 +91,68 @@ export function ObjectTable({ return ; }; - const renderTableHeader = (item: PropertyMetaInformation, key: number, allowSort: boolean): JSX.Element => { + const getDisabledText = (requirements: SortRequirements): null | JSX.Element => { + const missingFilters = getMissingFilters(requirements.requiredFilters, currentFilters); + + const missingSorts: string[] = []; + for (const sort of requirements.requiredSorts) { + if (!currentSorts.find(x => x.path === sort)) { + missingSorts.push(sort); + } + } + + if (missingFilters.length === 0 && missingSorts.length === 0) { + return null; + } + + return ( +
+ + +
+ ); + }; + + const renderSortable = (item: PropertyMetaInformation) => { const name = item.name; - const content = - item.isSortable && allowSort ? ( - onChangeSortClick(name)}> - {name} - - ) : ( - name - ); + const disabledText = getDisabledText(item.requiredForSort); + const link = ( + onChangeSortClick(name)} + disabled={Boolean(disabledText)}> + {name} + + ); + if (!disabledText) { + return link; + } + + return ( +
+ {link} + {"\u00A0"} + disabledText} pos="right middle"> + } /> + +
+ ); + }; + const renderTableHeader = (item: PropertyMetaInformation, key: number, allowSort: boolean): JSX.Element => { + const name = item.name; + const content = item.isSortable && allowSort ? renderSortable(item) : name; return ( {content} diff --git a/db-viewer-ui/src/Components/ObjectTable/TooltipContent.tsx b/db-viewer-ui/src/Components/ObjectTable/TooltipContent.tsx new file mode 100644 index 00000000..4cbe0a5e --- /dev/null +++ b/db-viewer-ui/src/Components/ObjectTable/TooltipContent.tsx @@ -0,0 +1,61 @@ +import React from "react"; + +import { Condition } from "../../Domain/Api/DataTypes/Condition"; +import { FilterRequirement } from "../../Domain/Api/DataTypes/FilterRequirement"; +import { StringUtils } from "../../Domain/Utils/StringUtils"; + +import { jsStyles } from "./ObjectTable.styles"; + +interface MissingFiltersProps { + title: string; + missingFilters: FilterRequirement[]; +} + +export function getMissingFilters( + requiredFilters: FilterRequirement[], + currentFilters: Condition[] +): FilterRequirement[] { + const missingFilters: FilterRequirement[] = []; + for (const filter of requiredFilters) { + const currentFilter = currentFilters.find(x => x.path === filter.propertyName); + if ( + !currentFilter || + StringUtils.isNullOrWhitespace(currentFilter.value) || + filter.availableFilters.indexOf(currentFilter.operator) === -1 + ) { + missingFilters.push(filter); + } + } + return missingFilters; +} + +export function MissingFilters({ title, missingFilters }: MissingFiltersProps): null | JSX.Element { + return missingFilters.length === 0 ? null : ( +
+ {title} + {missingFilters.map(x => ( +
+ {x.propertyName}: {x.availableFilters.join("ИЛИ")} +
+ ))} +
+ ); +} + +interface MissingSortsProps { + title: string; + missingSorts: string[]; +} + +export function MissingSorts({ title, missingSorts }: MissingSortsProps): null | JSX.Element { + return missingSorts.length === 0 ? null : ( +
+ {title} + {missingSorts.map(x => ( +
+ {x} +
+ ))} +
+ ); +} diff --git a/db-viewer-ui/src/Containers/ObjectTableContainer.tsx b/db-viewer-ui/src/Containers/ObjectTableContainer.tsx index 29d29e5f..00af00ee 100644 --- a/db-viewer-ui/src/Containers/ObjectTableContainer.tsx +++ b/db-viewer-ui/src/Containers/ObjectTableContainer.tsx @@ -99,7 +99,7 @@ class ObjectTableContainerInternal extends React.Component { - const { sorts } = this.state.query; + const { metaInformation, query } = this.state; + + if (metaInformation == null) { + return; + } + + const dependents = metaInformation.typeMetaInformation.properties.filter( + x => x.requiredForSort.requiredSorts.indexOf(columnName) !== -1 + ); + + const sorts = query.sorts.filter(x => !dependents.find(d => d.name === x.path)); + const currentSortIndex = sorts.findIndex(x => x.path === columnName); if (currentSortIndex === -1) { this.updateQuery({ diff --git a/global.json b/global.json index d6c2c37f..1c7274b7 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "6.0.100", + "version": "7.0.100", "rollForward": "latestFeature" } } \ No newline at end of file