@@ -19,6 +19,16 @@ import Grid from '@mui/material/Grid'
1919import Paper from '@mui/material/Paper'
2020import { loader } from 'graphql.macro'
2121import React from 'react'
22+ import { useState , useEffect , useMemo } from 'react'
23+ import {
24+ Box ,
25+ Checkbox ,
26+ FormControl ,
27+ FormControlLabel ,
28+ InputLabel ,
29+ MenuItem ,
30+ Select
31+ } from '@mui/material'
2232import Node from '../../components/Node/Node'
2333import { useQuery } from '@apollo/client'
2434import NodeInfo from '../../models/node-info'
@@ -30,85 +40,137 @@ import StereotypeInfo from '../../models/stereotype-info'
3040import browserVersion from '../../util/browser-version'
3141import Capabilities from '../../models/capabilities'
3242import { GridConfig } from '../../config'
33- import { NODES_QUERY } from " ../../graphql/nodes" ;
43+ import { NODES_QUERY } from ' ../../graphql/nodes'
3444
3545function Overview ( ) : JSX . Element {
3646 const { loading, error, data } = useQuery ( NODES_QUERY , {
3747 pollInterval : GridConfig . status . xhrPollingIntervalMillis ,
3848 fetchPolicy : 'network-only'
3949 } )
4050
51+ const [ sortOption , setSortOption ] = useState ( 'osInfo.name' )
52+ const [ sortOrder , setSortOrder ] = useState ( 1 )
53+ const [ sortedNodes , setSortedNodes ] = useState < NodeInfo [ ] > ( [ ] )
54+ const [ isDescending , setIsDescending ] = useState ( false )
55+
56+ const handleSortChange = ( event : React . ChangeEvent < { value : unknown } > ) => {
57+ setSortOption ( event . target . value as string )
58+ }
59+
60+ const handleOrderChange = ( event : React . ChangeEvent < HTMLInputElement > ) => {
61+ setIsDescending ( event . target . checked )
62+ setSortOrder ( event . target . checked ? - 1 : 1 )
63+ }
64+
65+ const sortProperties = {
66+ 'osInfo.name' : ( a , b ) => a . osInfo . name . localeCompare ( b . osInfo . name ) ,
67+ 'status' : ( a , b ) => a . status . localeCompare ( b . status ) ,
68+ 'id' : ( a , b ) => ( a . id < b . id ? - 1 : 1 )
69+ }
70+
71+ const sortNodes = useMemo ( ( ) => {
72+ return ( nodes : NodeInfo [ ] , option : string , order : number ) => {
73+ const sortFn = sortProperties [ option ] || ( ( ) => 0 )
74+ return nodes . sort ( ( a , b ) => order * sortFn ( a , b ) )
75+ }
76+ } , [ sortOption , sortOrder ] )
77+
78+ useEffect ( ( ) => {
79+ if ( data ) {
80+ const unSortedNodes = data . nodesInfo . nodes . map ( ( node ) => {
81+ const osInfo : OsInfo = {
82+ name : node . osInfo . name ,
83+ version : node . osInfo . version ,
84+ arch : node . osInfo . arch
85+ }
86+
87+ interface StereoTypeData {
88+ stereotype : Capabilities ;
89+ slots : number ;
90+ }
91+
92+ const slotStereotypes = ( JSON . parse (
93+ node . stereotypes ) as StereoTypeData [ ] ) . map ( ( item ) => {
94+ const slotStereotype : StereotypeInfo = {
95+ browserName : item . stereotype . browserName ?? '' ,
96+ browserVersion : browserVersion (
97+ item . stereotype . browserVersion ?? item . stereotype . version ) ,
98+ platformName : ( item . stereotype . platformName
99+ ?? item . stereotype . platform ) ?? '' ,
100+ slotCount : item . slots ,
101+ rawData : item
102+ }
103+ return slotStereotype
104+ } )
105+
106+ const newNode : NodeInfo = {
107+ uri : node . uri ,
108+ id : node . id ,
109+ status : node . status ,
110+ maxSession : node . maxSession ,
111+ slotCount : node . slotCount ,
112+ version : node . version ,
113+ osInfo : osInfo ,
114+ sessionCount : node . sessionCount ?? 0 ,
115+ slotStereotypes : slotStereotypes
116+ }
117+ return newNode
118+ } )
119+
120+ setSortedNodes ( sortNodes ( unSortedNodes , sortOption , sortOrder ) )
121+ }
122+ } , [ data , sortOption , sortOrder ] )
123+
41124 if ( error !== undefined ) {
42125 const message = 'There has been an error while loading the Nodes from the Grid.'
43126 const errorMessage = error ?. networkError ?. message
44127 return (
45128 < Grid container spacing = { 3 } >
46- < Error message = { message } errorMessage = { errorMessage } />
129+ < Error message = { message } errorMessage = { errorMessage } />
47130 </ Grid >
48131 )
49132 }
50133
51134 if ( loading ) {
52135 return (
53136 < Grid container spacing = { 3 } >
54- < Loading />
137+ < Loading />
55138 </ Grid >
56139 )
57140 }
58141
59- const unSortedNodes = data . nodesInfo . nodes . map ( ( node ) => {
60- const osInfo : OsInfo = {
61- name : node . osInfo . name ,
62- version : node . osInfo . version ,
63- arch : node . osInfo . arch
64- }
65-
66- interface StereoTypeData {
67- stereotype : Capabilities
68- slots : number
69- }
70-
71- const slotStereotypes = ( JSON . parse (
72- node . stereotypes ) as StereoTypeData [ ] ) . map ( ( item ) => {
73- const slotStereotype : StereotypeInfo = {
74- browserName : item . stereotype . browserName ?? '' ,
75- browserVersion : browserVersion (
76- item . stereotype . browserVersion ?? item . stereotype . version ) ,
77- platformName : ( item . stereotype . platformName ??
78- item . stereotype . platform ) ?? '' ,
79- slotCount : item . slots ,
80- rawData : item
81- }
82- return slotStereotype
83- } )
84- const newNode : NodeInfo = {
85- uri : node . uri ,
86- id : node . id ,
87- status : node . status ,
88- maxSession : node . maxSession ,
89- slotCount : node . slotCount ,
90- version : node . version ,
91- osInfo : osInfo ,
92- sessionCount : node . sessionCount ?? 0 ,
93- slotStereotypes : slotStereotypes
94- }
95- return newNode
96- } )
97-
98- const nodes = unSortedNodes . sort ( ( a , b ) => ( a . id < b . id ? - 1 : 1 ) )
99- if ( nodes . length === 0 ) {
142+ if ( sortedNodes . length === 0 ) {
100143 const shortMessage = 'The Grid has no registered Nodes yet.'
101144 return (
102145 < Grid container spacing = { 3 } >
103- < NoData message = { shortMessage } />
146+ < NoData message = { shortMessage } />
104147 </ Grid >
105148 )
106149 }
107150
108151 return (
109152 < Grid container >
110- { /* Nodes */ }
111- { nodes . map ( ( node , index ) => {
153+ < Grid item xs = { 12 }
154+ style = { { display : 'flex' , justifyContent : 'flex-start' } } >
155+ < FormControl variant = "outlined" style = { { marginBottom : '16px' } } >
156+ < InputLabel > Sort By</ InputLabel >
157+ < Box display = "flex" alignItems = "center" >
158+ < Select value = { sortOption } onChange = { handleSortChange }
159+ label = "Sort By" style = { { minWidth : '150px' } } >
160+ < MenuItem value = "osInfo.name" > Platform</ MenuItem >
161+ < MenuItem value = "status" > Status</ MenuItem >
162+ < MenuItem value = "id" > ID</ MenuItem >
163+ </ Select >
164+ < FormControlLabel
165+ control = { < Checkbox checked = { isDescending }
166+ onChange = { handleOrderChange } /> }
167+ label = "Descending"
168+ style = { { marginLeft : '8px' } }
169+ />
170+ </ Box >
171+ </ FormControl >
172+ </ Grid >
173+ { sortedNodes . map ( ( node , index ) => {
112174 return (
113175 < Grid
114176 item
@@ -127,7 +189,7 @@ function Overview (): JSX.Element {
127189 flexDirection : 'column'
128190 } }
129191 >
130- < Node node = { node } />
192+ < Node node = { node } />
131193 </ Paper >
132194 </ Grid >
133195 )
0 commit comments