@ -1,4 +1,4 @@
import { useEffect , use State } from 'react'
import { useEffect , use Ref, use State } from 'react'
import { useNavigate , useSearchParams } from 'react-router-dom'
import { useNavigate , useSearchParams } from 'react-router-dom'
import { Badge , Button , DataTable , EmptyState , ErrorState , Field , FilterChips , Notice , PageHeader , Skeleton , Toolbar } from '@sims/ui'
import { Badge , Button , DataTable , EmptyState , ErrorState , Field , FilterChips , Notice , PageHeader , Skeleton , Toolbar } from '@sims/ui'
import { createClient , getClients , CLIENT_STATUSES , type ClientStatus , type DocStatus } from '../api'
import { createClient , getClients , CLIENT_STATUSES , type ClientStatus , type DocStatus } from '../api'
@ -9,9 +9,13 @@ export function useData<T>(loader: () => Promise<T>, deps: unknown[] = []): {
} {
} {
const [ data , setData ] = useState < T | undefined > ( )
const [ data , setData ] = useState < T | undefined > ( )
const [ error , setError ] = useState < string | undefined > ( )
const [ error , setError ] = useState < string | undefined > ( )
const seqRef = useRef ( 0 )
const reload = ( ) = > {
const reload = ( ) = > {
setError ( undefined )
setError ( undefined )
loader ( ) . then ( setData ) . catch ( ( e : Error ) = > setError ( e . message ) )
const mySeq = ++ seqRef . current
loader ( )
. then ( ( d ) = > { if ( seqRef . current === mySeq ) setData ( d ) } )
. catch ( ( e : Error ) = > { if ( seqRef . current === mySeq ) setError ( e . message ) } )
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect ( reload , deps )
useEffect ( reload , deps )
@ -34,12 +38,19 @@ export function Clients() {
const nav = useNavigate ( )
const nav = useNavigate ( )
const [ q , setQ ] = useState ( '' )
const [ q , setQ ] = useState ( '' )
const { data , error , reload } = useData ( ( ) = > getClients ( q ) , [ q ] )
const { data , error , reload } = useData ( ( ) = > getClients ( q ) , [ q ] )
const [ sp ] = useSearchParams ( )
const [ sp , setSp ] = useSearchParams ( )
const [ creating , setCreating ] = useState ( sp . get ( 'new' ) === '1' )
const [ creating , setCreating ] = useState ( sp . get ( 'new' ) === '1' )
const [ statusFilter , setStatusFilter ] = useState ( 'all' )
const [ statusFilter , setStatusFilter ] = useState ( 'all' )
useEffect ( ( ) = > {
if ( sp . get ( 'new' ) === '1' ) { setCreating ( true ) ; setSp ( { } , { replace : true } ) }
} , [ sp , setSp ] )
const counts = new Map < string , number > ( )
const counts = new Map < string , number > ( )
for ( const c of data ? ? [ ] ) counts . set ( c . status , ( counts . get ( c . status ) ? ? 0 ) + 1 )
for ( const c of data ? ? [ ] ) counts . set ( c . status , ( counts . get ( c . status ) ? ? 0 ) + 1 )
const shown = ( data ? ? [ ] ) . filter ( ( c ) = > statusFilter === 'all' || c . status === statusFilter )
const shown = ( data ? ? [ ] ) . filter ( ( c ) = > statusFilter === 'all' || c . status === statusFilter )
const why = [
q !== '' ? ` matching “ ${ q } ” ` : '' ,
statusFilter !== 'all' ? ` with status ${ statusFilter } ` : '' ,
] . filter ( ( x ) = > x !== '' ) . join ( '' )
return (
return (
< div className = "wf-page" >
< div className = "wf-page" >
@ -69,7 +80,7 @@ export function Clients() {
) }
) }
{ error !== undefined && < ErrorState message = { error } onRetry = { reload } / > }
{ error !== undefined && < ErrorState message = { error } onRetry = { reload } / > }
{ data === undefined && error === undefined ? < Skeleton rows = { 6 } / >
{ data === undefined && error === undefined ? < Skeleton rows = { 6 } / >
: shown . length === 0 ? < EmptyState > No clients { q !== '' ? ` matching “ ${ q } ” ` : statusFilter !== 'all' ? ` with status ${ statusFilter } ` : ' yet — add the first one' } . < / EmptyState > : (
: shown . length === 0 ? < EmptyState > No clients { why !== '' ? why : ' yet — add the first one' } . < / EmptyState > : (
< DataTable
< DataTable
columns = { [
columns = { [
{ key : 'code' , label : 'Code' , mono : true } , { key : 'name' , label : 'Name' } ,
{ key : 'code' , label : 'Code' , mono : true } , { key : 'name' , label : 'Name' } ,
@ -115,7 +126,7 @@ function NewClientForm(props: { onCreated: (id: string) => void }) {
}
}
return (
return (
< div style= { { border : '1px solid var(--border)' , borderRadius : 8 , padding : 12 , marginBottom : 12 } } >
< div className= "wf-card" >
< div style = { { display : 'flex' , gap : 10 , flexWrap : 'wrap' } } >
< div style = { { display : 'flex' , gap : 10 , flexWrap : 'wrap' } } >
< Field label = "Name" > < input className = "wf" value = { f . name } autoFocus onChange = { set ( 'name' ) } / > < / Field >
< Field label = "Name" > < input className = "wf" value = { f . name } autoFocus onChange = { set ( 'name' ) } / > < / Field >
< Field label = "State code" > < input className = "wf" style = { { width : 80 } } value = { f . stateCode } onChange = { set ( 'stateCode' ) } / > < / Field >
< Field label = "State code" > < input className = "wf" style = { { width : 80 } } value = { f . stateCode } onChange = { set ( 'stateCode' ) } / > < / Field >