mirror of
https://github.com/stashapp/stash.git
synced 2026-05-09 05:05:29 +02:00
Add sorting to sub-group UI
- Implemented sorting functionality for sub-groups based on their descriptions in the database queries. - Updated the GroupList component to allow sorting regardless of the view. - Changed default sorting in the GroupSubGroupsPanel to sub-group description. - Added localization for sub-group description in en-GB.json. - Included sub-group description in the list of sortable options in the groups filter model.
This commit is contained in:
parent
01a7583364
commit
cd6c54c4b8
7 changed files with 118 additions and 17 deletions
|
|
@ -500,6 +500,7 @@ var groupSortOptions = sortOptions{
|
|||
"rating",
|
||||
"scenes_count",
|
||||
"o_counter",
|
||||
"sub_group_description",
|
||||
"sub_group_order",
|
||||
"tag_count",
|
||||
"updated_at",
|
||||
|
|
@ -532,6 +533,14 @@ func (qb *GroupStore) setGroupSort(query *queryBuilder, findFilter *models.FindF
|
|||
query.joinSort(groupRelationsTable, "", "groups.id = groups_relations.sub_id")
|
||||
query.sortAndPagination += getSort("order_index", direction, groupRelationsTable)
|
||||
}
|
||||
case "sub_group_description":
|
||||
// as above, we need to handle parent groups differently here
|
||||
if query.hasJoin("groups_parents") {
|
||||
query.sortAndPagination += getSort("description", direction, "groups_parents")
|
||||
} else {
|
||||
query.joinSort(groupRelationsTable, "", "groups.id = groups_relations.sub_id")
|
||||
query.sortAndPagination += getSort("description", direction, groupRelationsTable)
|
||||
}
|
||||
case "tag_count":
|
||||
query.sortAndPagination += getCountSort(groupTable, groupsTagsTable, groupIDColumn, direction)
|
||||
case "scenes_count": // generic getSort won't work for this
|
||||
|
|
|
|||
|
|
@ -1124,6 +1124,90 @@ func TestGroupQuerySortOrderIndex(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestGroupQuerySortSubGroupDescription(t *testing.T) {
|
||||
runWithRollbackTxn(t, "sort subgroup description", func(t *testing.T, ctx context.Context) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cEmpty := models.Group{Name: "sort-desc-child-empty"}
|
||||
c01 := models.Group{Name: "sort-desc-child-01"}
|
||||
c2 := models.Group{Name: "sort-desc-child-2"}
|
||||
c10 := models.Group{Name: "sort-desc-child-10"}
|
||||
assert.NoError(db.Group.Create(ctx, &cEmpty))
|
||||
assert.NoError(db.Group.Create(ctx, &c01))
|
||||
assert.NoError(db.Group.Create(ctx, &c2))
|
||||
assert.NoError(db.Group.Create(ctx, &c10))
|
||||
|
||||
parent := models.Group{
|
||||
Name: "sort-desc-parent",
|
||||
SubGroups: models.NewRelatedGroupDescriptions([]models.GroupIDDescription{
|
||||
{GroupID: cEmpty.ID, Description: ""},
|
||||
{GroupID: c10.ID, Description: "10"},
|
||||
{GroupID: c2.ID, Description: "2"},
|
||||
{GroupID: c01.ID, Description: "01"},
|
||||
}),
|
||||
}
|
||||
assert.NoError(db.Group.Create(ctx, &parent))
|
||||
|
||||
sortKey := "sub_group_description"
|
||||
dirAsc := models.SortDirectionEnumAsc
|
||||
findFilter := models.FindFilterType{
|
||||
Sort: &sortKey,
|
||||
Direction: &dirAsc,
|
||||
}
|
||||
groupFilter := models.GroupFilterType{
|
||||
ContainingGroups: &models.HierarchicalMultiCriterionInput{
|
||||
Value: []string{strconv.Itoa(parent.ID)},
|
||||
Modifier: models.CriterionModifierIncludes,
|
||||
},
|
||||
}
|
||||
|
||||
groups, _, err := db.Group.Query(ctx, &groupFilter, &findFilter)
|
||||
assert.NoError(err)
|
||||
assert.Len(groups, 4)
|
||||
assert.Equal(cEmpty.ID, groups[0].ID)
|
||||
assert.Equal(c01.ID, groups[1].ID)
|
||||
assert.Equal(c2.ID, groups[2].ID)
|
||||
assert.Equal(c10.ID, groups[3].ID)
|
||||
|
||||
dirDesc := models.SortDirectionEnumDesc
|
||||
findFilter.Direction = &dirDesc
|
||||
groups, _, err = db.Group.Query(ctx, &groupFilter, &findFilter)
|
||||
assert.NoError(err)
|
||||
assert.Len(groups, 4)
|
||||
assert.Equal(c10.ID, groups[0].ID)
|
||||
assert.Equal(c2.ID, groups[1].ID)
|
||||
assert.Equal(c01.ID, groups[2].ID)
|
||||
assert.Equal(cEmpty.ID, groups[3].ID)
|
||||
|
||||
// Exercise the non-groups_parents code path by filtering on name only.
|
||||
nameCriterion := models.StringCriterionInput{
|
||||
Value: "sort-desc-child-",
|
||||
Modifier: models.CriterionModifierIncludes,
|
||||
}
|
||||
nameFilter := models.GroupFilterType{
|
||||
Name: &nameCriterion,
|
||||
}
|
||||
|
||||
findFilter.Direction = &dirAsc
|
||||
groups, _, err = db.Group.Query(ctx, &nameFilter, &findFilter)
|
||||
assert.NoError(err)
|
||||
assert.Len(groups, 4)
|
||||
assert.Equal(cEmpty.ID, groups[0].ID)
|
||||
assert.Equal(c01.ID, groups[1].ID)
|
||||
assert.Equal(c2.ID, groups[2].ID)
|
||||
assert.Equal(c10.ID, groups[3].ID)
|
||||
|
||||
findFilter.Direction = &dirDesc
|
||||
groups, _, err = db.Group.Query(ctx, &nameFilter, &findFilter)
|
||||
assert.NoError(err)
|
||||
assert.Len(groups, 4)
|
||||
assert.Equal(c10.ID, groups[0].ID)
|
||||
assert.Equal(c2.ID, groups[1].ID)
|
||||
assert.Equal(c01.ID, groups[2].ID)
|
||||
assert.Equal(cEmpty.ID, groups[3].ID)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGroupUpdateFrontImage(t *testing.T) {
|
||||
if err := withRollbackTxn(func(ctx context.Context) error {
|
||||
qb := db.Group
|
||||
|
|
|
|||
|
|
@ -88,6 +88,23 @@ func getSortDirection(direction string) string {
|
|||
return direction
|
||||
}
|
||||
}
|
||||
|
||||
func isNaturalSort(sort string) bool {
|
||||
switch sort {
|
||||
case "name", "title", "description":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func isCoalesceSort(column, sort string) string {
|
||||
if sort == "description" {
|
||||
return coalesce(column)
|
||||
}
|
||||
return column
|
||||
}
|
||||
|
||||
func getSort(sort string, direction string, tableName string) string {
|
||||
direction = getSortDirection(direction)
|
||||
|
||||
|
|
@ -115,11 +132,8 @@ func getSort(sort string, direction string, tableName string) string {
|
|||
if strings.Contains(sort, ".") {
|
||||
colName = sort
|
||||
}
|
||||
if strings.Compare(sort, "name") == 0 {
|
||||
return " ORDER BY " + colName + " COLLATE NATURAL_CI " + direction
|
||||
}
|
||||
if strings.Compare(sort, "title") == 0 {
|
||||
return " ORDER BY " + colName + " COLLATE NATURAL_CI " + direction
|
||||
if isNaturalSort(sort) {
|
||||
return " ORDER BY " + isCoalesceSort(colName, sort) + " COLLATE NATURAL_CI " + direction
|
||||
}
|
||||
|
||||
return " ORDER BY " + colName + " " + direction
|
||||
|
|
|
|||
|
|
@ -54,9 +54,6 @@ const useContainingGroupFilterHook = (
|
|||
filter.criteria.push(groupCriterion);
|
||||
}
|
||||
|
||||
filter.sortBy = "sub_group_order";
|
||||
filter.sortDirection = GQL.SortDirectionEnum.Asc;
|
||||
|
||||
return filter;
|
||||
};
|
||||
};
|
||||
|
|
@ -68,15 +65,10 @@ interface IGroupSubGroupsPanel {
|
|||
}
|
||||
|
||||
const defaultFilter = (() => {
|
||||
const sortBy = "sub_group_order";
|
||||
const ret = new ListFilterModel(GQL.FilterMode.Groups, undefined, {
|
||||
defaultSortBy: sortBy,
|
||||
return new ListFilterModel(GQL.FilterMode.Groups, undefined, {
|
||||
defaultSortBy: "sub_group_description",
|
||||
defaultSortDir: GQL.SortDirectionEnum.Asc,
|
||||
});
|
||||
|
||||
// unset the sort by so that its not included in the URL
|
||||
ret.sortBy = undefined;
|
||||
|
||||
return ret;
|
||||
})();
|
||||
|
||||
export const GroupSubGroupsPanel: React.FC<IGroupSubGroupsPanel> =
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ export const FilteredGroupList = PatchComponent(
|
|||
|
||||
const withSidebar = view !== View.GroupSubGroups;
|
||||
const filterable = view !== View.GroupSubGroups;
|
||||
const sortable = view !== View.GroupSubGroups;
|
||||
const sortable = true;
|
||||
|
||||
// States
|
||||
const {
|
||||
|
|
|
|||
|
|
@ -1611,6 +1611,7 @@
|
|||
"sub_group_count": "Sub-Group Count",
|
||||
"sub_group_of": "Sub-group of {parent}",
|
||||
"sub_group_order": "Sub-Group Order",
|
||||
"sub_group_description": "Sub-Group Description",
|
||||
"sub_groups": "Sub-Groups",
|
||||
"sub_tag_count": "Sub-Tag Count",
|
||||
"sub_tag_of": "Sub-tag of {parent}",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ const sortByOptions = [
|
|||
"duration",
|
||||
"rating",
|
||||
"tag_count",
|
||||
"sub_group_description",
|
||||
"sub_group_order",
|
||||
]
|
||||
.map(ListFilterOptions.createSortBy)
|
||||
|
|
|
|||
Loading…
Reference in a new issue