@ -1,7 +1,7 @@
import { useEffect , useState } from 'react'
import { useNavigate , useSearchParams } from 'react-router-dom'
import { Badge , Button , DataTable , EmptyState , Field, Notice, PageHeader , Toolbar } from '@sims/ui'
import { createClient , getClients , type ClientStatus , type DocStatus } from '../api'
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'
/** Tiny fetch-render hook shared by the HQ pages (same shape as backoffice live.tsx). */
export function useData < T > ( loader : ( ) = > Promise < T > , deps : unknown [ ] = [ ] ) : {
@ -36,6 +36,10 @@ export function Clients() {
const { data , error , reload } = useData ( ( ) = > getClients ( q ) , [ q ] )
const [ sp ] = useSearchParams ( )
const [ creating , setCreating ] = useState ( sp . get ( 'new' ) === '1' )
const [ statusFilter , setStatusFilter ] = useState ( 'all' )
const counts = new Map < string , number > ( )
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 )
return (
< div className = "wf-page" >
@ -49,22 +53,31 @@ export function Clients() {
className = "wf" style = { { maxWidth : 280 } } placeholder = "Search name / code / GSTIN…"
value = { q } onChange = { ( e ) = > setQ ( e . target . value ) }
/ >
< Badge > { data ? . length ? ? '…' } clients < / Badge >
< FilterChips
active = { statusFilter }
onChange = { setStatusFilter }
chips = { [
{ key : 'all' , label : 'All' , count : data?.length ? ? 0 } ,
. . . CLIENT_STATUSES . map ( ( s ) = > ( { key : s , label : s , count : counts.get ( s ) ? ? 0 } ) ) ,
] }
/ >
< span className = "spacer" / >
< Badge > { shown . length } shown < / Badge >
< / Toolbar >
{ creating && (
< NewClientForm onCreated = { ( id ) = > { setCreating ( false ) ; reload ( ) ; nav ( ` /clients/ ${ id } ` ) } } / >
) }
{ error !== undefined && < Notice tone = "err" > { error } < / Notice > }
{ data === undefined ? < EmptyState > Loading … < / EmptyState >
: data . length === 0 ? < EmptyState > No clients { q !== '' ? ` matching “ ${ q } ” ` : ' yet — add the first one' } . < / EmptyState > : (
{ error !== undefined && < ErrorState message = { error } onRetry = { reload } / > }
{ 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 > : (
< DataTable
columns = { [
{ key : 'code' , label : 'Code' } , { key : 'name' , label : 'Name' } ,
{ key : 'code' , label : 'Code' , mono : true } , { key : 'name' , label : 'Name' } ,
{ key : 'status' , label : 'Status' } , { key : 'state' , label : 'State' } ,
{ key : 'contact' , label : 'Contact' } ,
] }
onRowClick = { ( _row , i ) = > nav ( ` /clients/ ${ data [ i ] ! . id } ` ) }
rows = { data . map ( ( c ) = > ( {
onRowClick = { ( _row , i ) = > nav ( ` /clients/ ${ shown [ i ] ! . id } ` ) }
rows = { shown . map ( ( c ) = > ( {
code : c.code , name : c.name ,
status : < Badge tone = { CLIENT_TONE [ c . status ] } > { c . status } < / Badge > ,
state : c.stateCode ,