1+ import React , { useState , useMemo } from 'react' ;
2+ import supportData from '../pages/endToEndSupport.json' ;
3+ import styles from './EndToEndSupportTable.module.css' ;
4+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' ;
5+
6+ const statusClass = ( status ) => {
7+ switch ( status ) {
8+ case 'SUPPORTED' :
9+ return styles . statusSupported ;
10+ case 'NOT SUPPORTED' :
11+ return styles . statusNotSupported ;
12+ default :
13+ return '' ;
14+ }
15+ } ;
16+
17+ const getOsIconProps = ( os ) => {
18+ switch ( os ) {
19+ case 'macOS' :
20+ case 'iOS' :
21+ return [ 'fab' , 'apple' ] ;
22+ case 'Windows' :
23+ return [ 'fab' , 'microsoft' ] ;
24+ case 'Android' :
25+ return [ 'fab' , 'android' ] ;
26+ case 'Ubuntu' :
27+ return [ 'fab' , 'ubuntu' ] ;
28+ default :
29+ return null ;
30+ }
31+ } ;
32+
33+ const getBrowserIconProps = ( browser ) => {
34+ switch ( browser ) {
35+ case 'Chrome' :
36+ return [ 'fab' , 'chrome' ] ;
37+ case 'Safari' :
38+ return [ 'fab' , 'safari' ] ;
39+ case 'Edge' :
40+ return [ 'fab' , 'edge' ] ;
41+ default :
42+ return null ;
43+ }
44+ } ;
45+
46+ const useSortableData = ( items , config = null ) => {
47+ const [ sortConfig , setSortConfig ] = useState ( config ) ;
48+
49+ const sortedItems = useMemo ( ( ) => {
50+ let sortableItems = [ ...items ] ;
51+ if ( sortConfig !== null ) {
52+ sortableItems . sort ( ( a , b ) => {
53+ if ( a [ sortConfig . key ] < b [ sortConfig . key ] ) {
54+ return sortConfig . direction === 'ascending' ? - 1 : 1 ;
55+ }
56+ if ( a [ sortConfig . key ] > b [ sortConfig . key ] ) {
57+ return sortConfig . direction === 'ascending' ? 1 : - 1 ;
58+ }
59+ return 0 ;
60+ } ) ;
61+ }
62+ return sortableItems ;
63+ } , [ items , sortConfig ] ) ;
64+
65+ const requestSort = ( key ) => {
66+ let direction = 'ascending' ;
67+ if (
68+ sortConfig &&
69+ sortConfig . key === key &&
70+ sortConfig . direction === 'ascending'
71+ ) {
72+ direction = 'descending' ;
73+ }
74+ setSortConfig ( { key, direction } ) ;
75+ } ;
76+
77+ return { items : sortedItems , requestSort, sortConfig } ;
78+ } ;
79+
80+ const columnConfig = [
81+ { header : 'Client OS' , key : 'clientOs' } ,
82+ { header : 'Client Browser' , key : 'clientBrowser' } ,
83+ { header : 'Type & Flow' , key : 'typeFlow' } ,
84+ { header : 'Protocol' , key : 'protocol' } ,
85+ { header : 'CM Device' , key : 'credentialManagerDevice' } ,
86+ { header : 'CM' , key : 'credentialManager' } ,
87+ { header : 'Status' , key : 'status' } ,
88+ { header : 'Reason & Solution' , key : 'reason' } ,
89+ ] ;
90+
91+ export default function EndToEndSupportTable ( ) {
92+ const { rows, legend } = supportData ;
93+ const { items : sortedRows , requestSort, sortConfig } = useSortableData ( rows ) ;
94+ const abbreviations = legend ? legend . abbreviations || { } : { } ;
95+ const abbreviationItems = Object . entries ( abbreviations ) ;
96+
97+ const getSortDirectionClass = ( key ) => {
98+ if ( ! sortConfig || sortConfig . key !== key ) {
99+ return '' ;
100+ }
101+ return sortConfig . direction === 'ascending' ? styles . ascending : styles . descending ;
102+ } ;
103+
104+ return (
105+ < >
106+ { abbreviationItems . length > 0 && (
107+ < div className = "card margin-bottom--lg" >
108+ < div className = "card__header" >
109+ < h4 > Table Legend</ h4 >
110+ </ div >
111+ < div className = "card__body" >
112+ < div className = { styles . legend } >
113+ { abbreviationItems . map ( ( [ key , value ] , index ) => (
114+ < React . Fragment key = { key } >
115+ < div className = { styles . legendItem } >
116+ < strong > { key } :</ strong > { value }
117+ </ div >
118+ { index < abbreviationItems . length - 1 && (
119+ < div className = { styles . divider } > •</ div >
120+ ) }
121+ </ React . Fragment >
122+ ) ) }
123+ </ div >
124+ </ div >
125+ </ div >
126+ ) }
127+ < div style = { { overflowX : 'auto' } } >
128+ < table >
129+ < thead >
130+ < tr >
131+ { columnConfig . map ( ( { header, key } ) => {
132+ const classNames = [ styles . sortableHeader , getSortDirectionClass ( key ) ] ;
133+ if ( key !== 'reason' ) {
134+ classNames . push ( styles . cellCentered ) ;
135+ }
136+ return (
137+ < th
138+ key = { key }
139+ onClick = { ( ) => requestSort ( key ) }
140+ className = { classNames . join ( ' ' ) }
141+ >
142+ { header }
143+ </ th >
144+ ) ;
145+ } ) }
146+ </ tr >
147+ </ thead >
148+ < tbody >
149+ { sortedRows . map ( ( row , index ) => (
150+ < tr key = { index } >
151+ { columnConfig . map ( ( { key } ) => {
152+ const classNames = [
153+ key !== 'reason' ? styles . cellCentered : '' ,
154+ [ 'clientOs' , 'clientBrowser' , 'credentialManagerDevice' ] . includes ( key ) ? styles . noWrapCell : '' ,
155+ ]
156+ . filter ( Boolean )
157+ . join ( ' ' ) ;
158+
159+ let content ;
160+ const cellValue = row [ key ] ;
161+ let iconProps ;
162+
163+ if ( key === 'clientOs' || key === 'credentialManagerDevice' ) {
164+ iconProps = getOsIconProps ( cellValue ) ;
165+ } else if ( key === 'clientBrowser' ) {
166+ iconProps = getBrowserIconProps ( cellValue ) ;
167+ }
168+
169+ if ( iconProps ) {
170+ content = < >
171+ < FontAwesomeIcon icon = { iconProps } style = { { marginRight : '8px' } } />
172+ { cellValue }
173+ </ > ;
174+ } else if ( key === 'status' ) {
175+ content =
176+ < span className = { `${ styles . statusBadge } ${ statusClass ( row [ key ] ) } ` } >
177+ { row [ key ] }
178+ </ span > ;
179+ } else {
180+ content = cellValue ;
181+ }
182+
183+ return (
184+ < td key = { key } className = { classNames } >
185+ { content }
186+ </ td >
187+ ) ;
188+ } ) }
189+ </ tr >
190+ ) ) }
191+ </ tbody >
192+ </ table >
193+ </ div >
194+ </ >
195+ ) ;
196+ }
0 commit comments