22
33import { OnboardingLoadingAnimation } from '@/components/onboarding-loading-animation' ;
44import { useApi } from '@/hooks/use-api' ;
5+ import { useVendors , type Vendor } from '@/hooks/use-vendors' ;
56import { VendorStatus } from '@/components/vendor-status' ;
67import {
78 AlertDialog ,
@@ -41,23 +42,27 @@ import {
4142import { OverflowMenuVertical , Search , TrashCan } from '@trycompai/design-system/icons' ;
4243import { ArrowDown , ArrowUp , ArrowUpDown , Loader2 , UserIcon } from 'lucide-react' ;
4344import { useRouter } from 'next/navigation' ;
44- import { useCallback , useEffect , useMemo , useState } from 'react' ;
45+ import { useEffect , useMemo , useState } from 'react' ;
4546import { toast } from 'sonner' ;
46- import useSWR from 'swr' ;
47- import { getVendorsAction , type GetVendorsActionInput } from '../actions/get-vendors-action' ;
48- import type { GetAssigneesResult , GetVendorsResult } from '../data/queries' ;
49- import type { GetVendorsSchema } from '../data/validations' ;
47+ import { useSWRConfig } from 'swr' ;
5048import { useOnboardingStatus } from '../hooks/use-onboarding-status' ;
5149import { VendorOnboardingProvider , useVendorOnboardingStatus } from './vendor-onboarding-context' ;
5250
53- export type VendorRow = GetVendorsResult [ 'data' ] [ number ] & {
51+ export type VendorRow = Vendor & {
5452 isPending ?: boolean ;
5553 isAssessing ?: boolean ;
5654} ;
5755
58- const callGetVendorsAction = getVendorsAction as unknown as (
59- input : GetVendorsActionInput ,
60- ) => Promise < GetVendorsResult > ;
56+ type AssigneeMember = {
57+ id : string ;
58+ role : string ;
59+ user : {
60+ id : string ;
61+ name : string | null ;
62+ email : string ;
63+ image : string | null ;
64+ } ;
65+ } ;
6166
6267const ACTIVE_STATUSES : Array < 'pending' | 'processing' | 'created' | 'assessing' > = [
6368 'pending' ,
@@ -78,15 +83,13 @@ const CATEGORY_MAP: Record<string, string> = {
7883} ;
7984
8085interface VendorsTableProps {
81- vendors : GetVendorsResult [ 'data' ] ;
82- pageCount : number ;
83- assignees : GetAssigneesResult ;
86+ vendors : Vendor [ ] ;
87+ assignees : AssigneeMember [ ] ;
8488 onboardingRunId ?: string | null ;
85- searchParams : GetVendorsSchema ;
8689 orgId : string ;
8790}
8891
89- function VendorNameCell ( { vendor, orgId } : { vendor : VendorRow ; orgId : string } ) {
92+ function VendorNameCell ( { vendor } : { vendor : VendorRow } ) {
9093 const onboardingStatus = useVendorOnboardingStatus ( ) ;
9194 const status = onboardingStatus [ vendor . id ] ;
9295 const isPending = vendor . isPending || status === 'pending' || status === 'processing' ;
@@ -139,13 +142,13 @@ function VendorStatusCell({ vendor }: { vendor: VendorRow }) {
139142
140143export function VendorsTable ( {
141144 vendors : initialVendors ,
142- pageCount : initialPageCount ,
143145 assignees,
144146 onboardingRunId,
145147 orgId,
146148} : VendorsTableProps ) {
147149 const router = useRouter ( ) ;
148150 const api = useApi ( ) ;
151+ const { mutate : globalMutate } = useSWRConfig ( ) ;
149152 const [ deleteDialogOpen , setDeleteDialogOpen ] = useState ( false ) ;
150153 const [ vendorToDelete , setVendorToDelete ] = useState < VendorRow | null > ( null ) ;
151154 const [ isDeleting , setIsDeleting ] = useState ( false ) ;
@@ -165,44 +168,16 @@ export function VendorsTable({
165168 'vendors' ,
166169 ) ;
167170
168- // Build search params for API
169- const currentSearchParams = useMemo < GetVendorsSchema > ( ( ) => {
170- return {
171- page,
172- perPage,
173- name : '' ,
174- status : null ,
175- department : null ,
176- assigneeId : null ,
177- sort : [ { id : sort . id , desc : sort . desc } ] ,
178- filters : [ ] ,
179- joinOperator : 'and' ,
180- } ;
181- } , [ page , perPage , sort ] ) ;
182-
183- // Create stable SWR key
184- const swrKey = useMemo ( ( ) => {
185- if ( ! orgId ) return null ;
186- const key = JSON . stringify ( currentSearchParams ) ;
187- return [ 'vendors' , orgId , key ] as const ;
188- } , [ orgId , currentSearchParams ] ) ;
189-
190- // Fetcher function for SWR
191- const fetcher = useCallback ( async ( ) => {
192- if ( ! orgId ) return { data : [ ] , pageCount : 0 } ;
193- return await callGetVendorsAction ( { orgId, searchParams : currentSearchParams } ) ;
194- } , [ orgId , currentSearchParams ] ) ;
195-
196- // Use SWR to fetch vendors with polling for real-time updates
197- const { data : vendorsData } = useSWR ( swrKey , fetcher , {
198- fallbackData : { data : initialVendors , pageCount : initialPageCount } ,
171+ // Use SWR hook for vendors with polling
172+ const { data : vendorsResponse } = useVendors ( {
173+ initialData : initialVendors ,
199174 refreshInterval : isActive ? 1000 : 5000 ,
200- revalidateOnFocus : false ,
201- revalidateOnReconnect : true ,
202- keepPreviousData : true ,
203175 } ) ;
204176
205- const vendors = vendorsData ?. data || initialVendors ;
177+ const vendors = useMemo ( ( ) => {
178+ const data = vendorsResponse ?. data ?. data ;
179+ return Array . isArray ( data ) ? data : initialVendors ;
180+ } , [ vendorsResponse , initialVendors ] ) ;
206181
207182 // Check if all vendors are done assessing
208183 const allVendorsDoneAssessing = useMemo ( ( ) => {
@@ -276,8 +251,8 @@ export function VendorsTable({
276251 organizationId : orgId ,
277252 assigneeId : null ,
278253 assignee : null ,
279- createdAt : new Date ( ) ,
280- updatedAt : new Date ( ) ,
254+ createdAt : new Date ( ) . toISOString ( ) ,
255+ updatedAt : new Date ( ) . toISOString ( ) ,
281256 isPending : true ,
282257 } ) ) ;
283258
@@ -298,8 +273,8 @@ export function VendorsTable({
298273 organizationId : orgId ,
299274 assigneeId : null ,
300275 assignee : null ,
301- createdAt : new Date ( ) ,
302- updatedAt : new Date ( ) ,
276+ createdAt : new Date ( ) . toISOString ( ) ,
277+ updatedAt : new Date ( ) . toISOString ( ) ,
303278 isPending : true ,
304279 } ) ) ;
305280
@@ -330,24 +305,21 @@ export function VendorsTable({
330305 const comparison = ( aValue as string ) . localeCompare ( bValue as string ) ;
331306 return sort . desc ? - comparison : comparison ;
332307 }
333- const comparison = new Date ( aValue as Date ) . getTime ( ) - new Date ( bValue as Date ) . getTime ( ) ;
308+ const comparison =
309+ new Date ( aValue as string ) . getTime ( ) - new Date ( bValue as string ) . getTime ( ) ;
334310 return sort . desc ? - comparison : comparison ;
335311 } ) ;
336312
337313 return result ;
338314 } , [ mergedVendors , searchQuery , sort ] ) ;
339315
340316 // Calculate pageCount from filtered data and paginate
341- // When searching locally, calculate pageCount from filtered data
342- // When not searching, use server's pageCount (server handles pagination)
343- const filteredPageCount = searchQuery
344- ? Math . max ( 1 , Math . ceil ( filteredAndSortedVendors . length / perPage ) )
345- : Math . max ( 1 , vendorsData ?. pageCount ?? initialPageCount ) ;
346-
347- // When searching locally, slice the data for client-side pagination
348- // When not searching, server returns the correct page, but slice to enforce perPage
349- // (avoids extra rows from onboarding pending/temp vendors)
350- const startIndex = searchQuery ? ( page - 1 ) * perPage : 0 ;
317+ const filteredPageCount = Math . max (
318+ 1 ,
319+ Math . ceil ( filteredAndSortedVendors . length / perPage ) ,
320+ ) ;
321+
322+ const startIndex = ( page - 1 ) * perPage ;
351323 const paginatedVendors = filteredAndSortedVendors . slice ( startIndex , startIndex + perPage ) ;
352324
353325 // Keep page in bounds when pageCount changes
@@ -426,6 +398,13 @@ export function VendorsTable({
426398 toast . success ( 'Vendor deleted successfully' ) ;
427399 setDeleteDialogOpen ( false ) ;
428400 setVendorToDelete ( null ) ;
401+ globalMutate (
402+ ( key ) =>
403+ ( Array . isArray ( key ) && key [ 0 ] ?. includes ( '/v1/vendors' ) ) ||
404+ ( typeof key === 'string' && key . includes ( '/v1/vendors' ) ) ,
405+ undefined ,
406+ { revalidate : true } ,
407+ ) ;
429408 } else {
430409 toast . error ( 'Failed to delete vendor' ) ;
431410 }
@@ -555,7 +534,7 @@ export function VendorsTable({
555534 data-state = { blocked ? 'disabled' : undefined }
556535 >
557536 < TableCell >
558- < VendorNameCell vendor = { vendor } orgId = { orgId } />
537+ < VendorNameCell vendor = { vendor } />
559538 </ TableCell >
560539 < TableCell >
561540 < VendorStatusCell vendor = { vendor } />
0 commit comments