1+ import { useCallback , useMemo } from 'react'
2+ import type { Product } from '../utils/productsSchema'
3+ import { ProductLogo } from './ProductLogo'
4+ import { useProducts } from '../hooks/useProducts'
5+ import { VirtualizedTable } from './VirtualizedTable'
6+ import type { ColumnDef } from '@tanstack/react-table'
7+ import { cmsProductPricingSortingFn , getCMSProductPricingPreview } from '../utils/cmsProductPricing'
8+
9+ const useProductHelpers = ( ) => {
10+ const getPricingDisplay = useCallback ( ( product : Product ) => getCMSProductPricingPreview ( product ) , [ ] )
11+
12+ const getHeadquarters = useCallback ( ( product : Product ) => {
13+ if ( Array . isArray ( product . headquarters ) ) {
14+ if ( product . headquarters . length > 1 ) {
15+ return `${ product . headquarters [ 0 ] } (+${ product . headquarters . length - 1 } )`
16+ }
17+ return product . headquarters [ 0 ]
18+ }
19+ return product . headquarters
20+ } , [ ] )
21+
22+ const getScreensDisplay = useCallback ( ( product : Product ) => {
23+ const screensTotal = product . stats ?. screens ?. total
24+ if ( screensTotal && typeof screensTotal === 'number' ) {
25+ return screensTotal
26+ }
27+ return '-'
28+ } , [ ] )
29+
30+ return { getPricingDisplay, getHeadquarters, getScreensDisplay }
31+ }
32+
33+ const useTableColumns = ( ) => {
34+ const { getPricingDisplay, getHeadquarters, getScreensDisplay } = useProductHelpers ( )
35+
36+ return useMemo < ColumnDef < Product > [ ] > (
37+ ( ) => [
38+ {
39+ header : 'Product' ,
40+ accessorKey : 'name' ,
41+ size : 500 ,
42+ sortingFn : 'alphanumeric' ,
43+ cell : ( { row } ) => {
44+ const product = row . original
45+ return (
46+ < a
47+ href = { product . website }
48+ target = "_blank"
49+ rel = "noopener nofollow ugc"
50+ className = "flex items-center gap-3 group"
51+ >
52+ < ProductLogo product = { product } />
53+ < div className = "flex flex-col" >
54+ < div className = "flex items-center gap-2 font-medium text-gray-900 group-hover:text-blue-600" >
55+ { product . name }
56+ </ div >
57+ { product . description && (
58+ < div className = "text-sm text-gray-500 mt-1 max-w-md" >
59+ { product . description }
60+ </ div >
61+ ) }
62+ </ div >
63+ </ a >
64+ )
65+ } ,
66+ } ,
67+ {
68+ header : 'Pricing per screen' ,
69+ accessorFn : ( row ) => getPricingDisplay ( row ) ,
70+ size : 200 ,
71+ sortingFn : cmsProductPricingSortingFn ,
72+ cell : ( { getValue } ) => (
73+ < span className = "text-sm text-gray-600" > { getValue ( ) as string } </ span >
74+ ) ,
75+ } ,
76+ {
77+ header : 'Signup' ,
78+ accessorFn : ( row ) => row . self_signup ? 'Self-signup' : 'On request' ,
79+ size : 150 ,
80+ cell : ( { getValue } ) => (
81+ < span className = "text-sm text-gray-600" > { getValue ( ) as string } </ span >
82+ ) ,
83+ } ,
84+ {
85+ header : 'Headquarters' ,
86+ accessorFn : ( row ) => getHeadquarters ( row ) ,
87+ size : 200 ,
88+ cell : ( { getValue } ) => (
89+ < span className = "text-sm text-gray-600" > { getValue ( ) as string } </ span >
90+ ) ,
91+ } ,
92+ {
93+ header : 'Screens' ,
94+ accessorFn : ( row ) => getScreensDisplay ( row ) ,
95+ size : 150 ,
96+ sortingFn : 'alphanumeric' ,
97+ cell : ( { getValue } ) => (
98+ < span className = "text-sm text-gray-600" > { `${ ( getValue ( ) as number ) > 0 ? ( getValue ( ) as number ) . toLocaleString ( ) + '+' : '-' } ` } </ span >
99+ ) ,
100+ } ,
101+ ] ,
102+ [ getPricingDisplay , getHeadquarters , getScreensDisplay ]
103+ )
104+ }
105+
106+ export const CMSList = ( ) => {
107+ const products = useProducts ( )
108+ const columns = useTableColumns ( )
109+
110+ return (
111+ < VirtualizedTable
112+ data = { products }
113+ columns = { columns }
114+ initialSorting = { [ { id : 'name' , desc : false } ] }
115+ />
116+ )
117+ }
0 commit comments