76f525fd38
- Migrate `EnvironmentList` from `GroupSortTable` to `SortableList`, removing ~1,700 lines of duplicated component code - Move health sort ranking to the backend (`sort.go`), adding `Health` and `Id` sort keys - Delete `GroupSortTable`, `GroupSortTableGroupRow`, `useGroupSortTableState`, and `store` — functionality absorbed by `SortableList` - Add `useHomeViewState` hook to centralise home view URL state (`groupBy`, `groupFilter`, `order`, `page`, `search`) - Update `useTableStateFromUrl` to support `groupBy` and `groupFilter` URL params with a `buildExtra` callback - Rename URL param `filter` → `groupFilter` for clarity; add `search` and `order` to `/home` route definition - Simplify `EnvironmentList` props — remove `headerFilter` / `onHeaderFilterChange`, leaving only `onClickBrowse` - Add `computeSortDesc` pure utility to `SortableList` and cover all toggle/reset cases with unit tests - Update `SortableListHeader` to use `activeKey` prop (renamed from `sortBy`); fix all callsites and stories - Fix `SortableList` sort-key normalisation to be case-insensitive; update tests to reflect no-match behaviour
125 lines
3.0 KiB
Go
125 lines
3.0 KiB
Go
package endpoints
|
|
|
|
import (
|
|
"slices"
|
|
|
|
"github.com/fvbommel/sortorder"
|
|
portainer "github.com/portainer/portainer/api"
|
|
"github.com/portainer/portainer/api/internal/endpointutils"
|
|
)
|
|
|
|
type comp[T any] func(a, b T) int
|
|
|
|
func stringComp(a, b string) int {
|
|
if sortorder.NaturalLess(a, b) {
|
|
return -1
|
|
} else if sortorder.NaturalLess(b, a) {
|
|
return 1
|
|
} else {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func healthRank(endpoint *portainer.Endpoint, settings *portainer.Settings) int {
|
|
status := resolveEndpointStatus(endpoint, settings)
|
|
if status == statusDown {
|
|
return 0
|
|
}
|
|
if isOutdated(endpoint) {
|
|
return 1
|
|
}
|
|
if status == statusHeartbeat {
|
|
return 2
|
|
}
|
|
return 3
|
|
}
|
|
|
|
func sortEnvironmentsByField(environments []portainer.Endpoint, environmentGroups []portainer.EndpointGroup, sortField sortKey, isSortDesc bool, settings *portainer.Settings) {
|
|
if sortField == "" {
|
|
return
|
|
}
|
|
|
|
var less comp[portainer.Endpoint]
|
|
switch sortField {
|
|
case sortKeyName:
|
|
less = func(a, b portainer.Endpoint) int {
|
|
return stringComp(a.Name, b.Name)
|
|
}
|
|
|
|
case sortKeyGroup:
|
|
environmentGroupNames := make(map[portainer.EndpointGroupID]string, 0)
|
|
for _, group := range environmentGroups {
|
|
environmentGroupNames[group.ID] = group.Name
|
|
}
|
|
|
|
// set the "unassigned" group name to be empty string
|
|
environmentGroupNames[1] = ""
|
|
|
|
less = func(a, b portainer.Endpoint) int {
|
|
aGroup := environmentGroupNames[a.GroupID]
|
|
bGroup := environmentGroupNames[b.GroupID]
|
|
|
|
return stringComp(aGroup, bGroup)
|
|
}
|
|
|
|
case sortKeyStatus:
|
|
less = func(a, b portainer.Endpoint) int {
|
|
return int(a.Status - b.Status)
|
|
}
|
|
|
|
case sortKeyLastCheckInDate:
|
|
less = func(a, b portainer.Endpoint) int {
|
|
return int(a.LastCheckInDate - b.LastCheckInDate)
|
|
}
|
|
case sortKeyEdgeID:
|
|
less = func(a, b portainer.Endpoint) int {
|
|
return stringComp(a.EdgeID, b.EdgeID)
|
|
}
|
|
|
|
case sortKeyPlatformType:
|
|
less = func(a, b portainer.Endpoint) int {
|
|
return int(endpointutils.EndpointPlatformType(&a) - endpointutils.EndpointPlatformType(&b))
|
|
}
|
|
case sortKeyHealth:
|
|
less = func(a, b portainer.Endpoint) int {
|
|
return healthRank(&a, settings) - healthRank(&b, settings)
|
|
}
|
|
case sortKeyId:
|
|
less = func(a, b portainer.Endpoint) int {
|
|
return int(a.ID - b.ID)
|
|
}
|
|
}
|
|
|
|
slices.SortStableFunc(environments, func(a, b portainer.Endpoint) int {
|
|
mul := 1
|
|
if isSortDesc {
|
|
mul = -1
|
|
}
|
|
|
|
return less(a, b) * mul
|
|
})
|
|
|
|
}
|
|
|
|
type sortKey string
|
|
|
|
const (
|
|
sortKeyName sortKey = "Name"
|
|
sortKeyGroup sortKey = "Group"
|
|
sortKeyStatus sortKey = "Status"
|
|
sortKeyLastCheckInDate sortKey = "LastCheckIn"
|
|
sortKeyEdgeID sortKey = "EdgeID"
|
|
sortKeyPlatformType sortKey = "PlatformType"
|
|
sortKeyHealth sortKey = "Health"
|
|
sortKeyId sortKey = "Id"
|
|
)
|
|
|
|
func getSortKey(sortField string) sortKey {
|
|
fieldAsSortKey := sortKey(sortField)
|
|
if slices.Contains([]sortKey{sortKeyName, sortKeyGroup, sortKeyStatus, sortKeyLastCheckInDate, sortKeyEdgeID, sortKeyPlatformType, sortKeyHealth, sortKeyId}, fieldAsSortKey) {
|
|
return fieldAsSortKey
|
|
}
|
|
|
|
return ""
|
|
}
|