@@ -2,10 +2,12 @@ import "./App.css";
22import { DataTable } from "./components/data-table" ;
33import React , { useEffect , useState } from "react" ;
44import { State } from "./gen/flowstate/v1/messages_pb" ;
5+ import { Select } from "./components/ui/select" ;
56
67import { ColumnDef } from "@tanstack/react-table" ;
78import { createDriverClient } from "./api" ;
89import { Badge } from "./components/ui/badge" ;
10+ import { Button } from "./components/ui/button" ;
911import {
1012 Dialog ,
1113 DialogContent ,
@@ -127,11 +129,43 @@ type DriverClient = ReturnType<typeof createDriverClient>;
127129
128130export const StatesPage = ( ) => {
129131 const [ states , setStates ] = useState < State [ ] > ( [ ] ) ;
132+ const [ refreshInterval , setRefreshInterval ] = useState < number > ( 5000 ) ;
130133 const client = React . useContext ( ApiContext ) ;
131134
135+ const manualRefresh = async ( ) => {
136+ if ( ! client ) return ;
137+
138+ const getStatesCommand = new GetStatesCommand ( {
139+ limit : BigInt ( 100 ) ,
140+ latestOnly : false ,
141+ sinceRev : BigInt ( 0 ) ,
142+ } ) ;
143+
144+ const anyCommand = new Command ( {
145+ getStates : getStatesCommand
146+ } ) ;
147+
148+ try {
149+ const anyCommandResp = await client . getStates ( anyCommand ) ;
150+ const getStatesCommand = anyCommandResp . getStates
151+ if ( getStatesCommand && getStatesCommand . result ) {
152+ const getStatesResult = getStatesCommand . result ;
153+ if ( getStatesResult . states . length > 0 ) {
154+ const sortedStates = getStatesResult . states . sort ( ( a , b ) => Number ( b . rev - a . rev ) ) ;
155+ setStates ( sortedStates ) ;
156+ }
157+ }
158+ } catch ( error ) {
159+ console . log ( "Manual refresh error" , error ) ;
160+ }
161+ } ;
162+
132163 useEffect ( ( ) => {
133164 if ( ! client ) return ;
134165
166+ // Clear existing states when refresh interval changes
167+ setStates ( [ ] ) ;
168+
135169 const abortController = new AbortController ( ) ;
136170
137171 listenToStates ( client , abortController . signal ) . catch ( ( error ) =>
@@ -141,7 +175,7 @@ export const StatesPage = () => {
141175 return ( ) => {
142176 abortController . abort ( ) ;
143177 } ;
144- } , [ client ] ) ;
178+ } , [ client , refreshInterval ] ) ;
145179
146180 const listenToStates = async ( client : DriverClient , signal : AbortSignal ) => {
147181 let sinceRev = BigInt ( 0 ) ;
@@ -185,10 +219,14 @@ export const StatesPage = () => {
185219
186220 await pollStates ( ) ;
187221
188- intervalId = setInterval ( pollStates , 1000 ) ;
222+ if ( refreshInterval > 0 ) {
223+ intervalId = setInterval ( pollStates , refreshInterval ) ;
224+ }
189225
190226 signal . addEventListener ( 'abort' , ( ) => {
191- clearInterval ( intervalId ) ;
227+ if ( intervalId ) {
228+ clearInterval ( intervalId ) ;
229+ }
192230 } ) ;
193231 } ;
194232
@@ -208,6 +246,34 @@ export const StatesPage = () => {
208246
209247 return (
210248 < div className = "container mx-auto py-10" >
249+ < div className = "flex justify-between items-center mb-4" >
250+ < h1 className = "text-2xl font-semibold" > States</ h1 >
251+ < div className = "flex items-center gap-2" >
252+ < label htmlFor = "refresh-interval" className = "text-sm font-medium" >
253+ Refresh:
254+ </ label >
255+ < Select
256+ id = "refresh-interval"
257+ value = { refreshInterval . toString ( ) }
258+ onChange = { ( e ) => setRefreshInterval ( Number ( e . target . value ) ) }
259+ className = "w-24"
260+ >
261+ < option value = "1000" > 1s</ option >
262+ < option value = "5000" > 5s</ option >
263+ < option value = "10000" > 10s</ option >
264+ < option value = "20000" > 20s</ option >
265+ < option value = "30000" > 30s</ option >
266+ < option value = "0" > Off</ option >
267+ </ Select >
268+ < Button
269+ onClick = { manualRefresh }
270+ variant = "outline"
271+ size = "sm"
272+ >
273+ Refresh
274+ </ Button >
275+ </ div >
276+ </ div >
211277 < DataTable columns = { columns } data = { data } />
212278 </ div >
213279 ) ;
0 commit comments