@@ -26,7 +26,7 @@ import { useAccess } from '../hooks/useAPI';
2626import { mockAccessResources } from '../mock/data' ;
2727
2828type SortDirection = 'asc' | 'desc' ;
29- type SortColumn = 'hostname' | 'status' | 'container_name ' | 'agent_id' | 'updated_at ';
29+ type SortColumn = 'hostname' | 'status' | 'access_policy_name ' | 'access_decision ' ;
3030
3131const statusOptions = [
3232 { label : 'All' , value : 'all' } ,
@@ -35,10 +35,17 @@ const statusOptions = [
3535 { label : 'Error' , value : 'error' } ,
3636] ;
3737
38+ const decisionColors : Record < string , string > = {
39+ allow : 'green' ,
40+ block : 'red' ,
41+ bypass : 'orange' ,
42+ service_auth : 'violet' ,
43+ } ;
44+
3845export function Access ( ) {
3946 const [ search , setSearch ] = useState ( '' ) ;
4047 const [ statusFilter , setStatusFilter ] = useState ( 'all' ) ;
41- const [ sortColumn , setSortColumn ] = useState < SortColumn > ( 'hostname ' ) ;
48+ const [ sortColumn , setSortColumn ] = useState < SortColumn > ( 'access_policy_name ' ) ;
4249 const [ sortDirection , setSortDirection ] = useState < SortDirection > ( 'asc' ) ;
4350 const [ selectedResource , setSelectedResource ] = useState < any > ( null ) ;
4451
@@ -69,7 +76,13 @@ export function Access() {
6976
7077 const filtered = useMemo ( ( ) => {
7178 let result = resources . filter ( ( r : any ) => {
72- if ( search && ! r . hostname . toLowerCase ( ) . includes ( search . toLowerCase ( ) ) ) return false ;
79+ if ( search ) {
80+ const q = search . toLowerCase ( ) ;
81+ const matchHostname = r . hostname ?. toLowerCase ( ) . includes ( q ) ;
82+ const matchPolicy = r . access_policy_name ?. toLowerCase ( ) . includes ( q ) ;
83+ const matchApp = r . access_app_name ?. toLowerCase ( ) . includes ( q ) ;
84+ if ( ! matchHostname && ! matchPolicy && ! matchApp ) return false ;
85+ }
7386 if ( useMock && statusFilter !== 'all' && r . status !== statusFilter ) return false ;
7487 return true ;
7588 } ) ;
@@ -99,7 +112,7 @@ export function Access() {
99112 size = "sm"
100113 />
101114 < TextInput
102- placeholder = "Search hostname..."
115+ placeholder = "Search policy or hostname..."
103116 leftSection = { < IconSearch size = { 16 } /> }
104117 value = { search }
105118 onChange = { ( e ) => setSearch ( e . currentTarget . value ) }
@@ -125,16 +138,31 @@ export function Access() {
125138 >
126139 Status
127140 </ SortableHeader >
141+ < SortableHeader
142+ sorted = { sortColumn === 'access_policy_name' }
143+ direction = { sortColumn === 'access_policy_name' ? sortDirection : null }
144+ onSort = { ( ) => handleSort ( 'access_policy_name' ) }
145+ >
146+ Policy
147+ </ SortableHeader >
128148 < SortableHeader
129149 sorted = { sortColumn === 'hostname' }
130150 direction = { sortColumn === 'hostname' ? sortDirection : null }
131151 onSort = { ( ) => handleSort ( 'hostname' ) }
132- style = { { maxWidth : 200 } }
152+ style = { { maxWidth : 220 } }
133153 >
134154 Hostname
135155 </ SortableHeader >
136- < Table . Th > App Name</ Table . Th >
137- { ! isMobile && < Table . Th > Policies</ Table . Th > }
156+ { ! isMobile && (
157+ < SortableHeader
158+ sorted = { sortColumn === 'access_decision' }
159+ direction = { sortColumn === 'access_decision' ? sortDirection : null }
160+ onSort = { ( ) => handleSort ( 'access_decision' ) }
161+ style = { { width : 110 } }
162+ >
163+ Decision
164+ </ SortableHeader >
165+ ) }
138166 { isMobile && < Table . Th style = { { width : 50 } } > </ Table . Th > }
139167 </ Table . Tr >
140168 </ Table . Thead >
@@ -180,28 +208,27 @@ export function Access() {
180208 < StatusBadge status = { r . status } iconOnly = { isMobile } />
181209 ) }
182210 </ Table . Td >
183- < Table . Td >
184- < Code > { r . hostname } </ Code >
185- </ Table . Td >
186211 < Table . Td >
187212 < Text size = "sm" fw = { 500 } >
188- { r . app_name ?? r . service_name ?? '—' }
213+ { r . access_policy_name || r . service_name || '—' }
189214 </ Text >
190215 </ Table . Td >
216+ < Table . Td >
217+ < Code > { r . hostname } </ Code >
218+ </ Table . Td >
191219 { ! isMobile && (
192220 < Table . Td >
193- < Group gap = { 4 } >
194- { ( r . policies ?? [ ] ) . slice ( 0 , 2 ) . map ( ( p : string , i : number ) => (
195- < Badge key = { i } variant = "light" size = "sm" color = "teal" >
196- { p }
197- </ Badge >
198- ) ) }
199- { ( r . policies ?. length ?? 0 ) > 2 && (
200- < Badge variant = "outline" size = "sm" color = "gray" >
201- +{ ( r . policies ?. length ?? 0 ) - 2 }
202- </ Badge >
203- ) }
204- </ Group >
221+ { r . access_decision ? (
222+ < Badge
223+ variant = "light"
224+ size = "sm"
225+ color = { decisionColors [ r . access_decision ] || 'gray' }
226+ >
227+ { r . access_decision }
228+ </ Badge >
229+ ) : (
230+ < Text size = "sm" c = "dimmed" > —</ Text >
231+ ) }
205232 </ Table . Td >
206233 ) }
207234 { isMobile && (
@@ -221,7 +248,7 @@ export function Access() {
221248
222249 < Group p = "md" justify = "space-between" >
223250 < Text size = "sm" c = "dimmed" >
224- Showing { filtered . length } policies
251+ Showing { filtered . length } access { filtered . length === 1 ? 'app' : 'apps' }
225252 </ Text >
226253 </ Group >
227254 </ Paper >
0 commit comments