@@ -5,11 +5,13 @@ import type { ColumnsType } from 'antd/es/table';
55import  type  {  CSSProperties ,  ReactNode  }  from  'react' ; 
66import  {  useMemo  }  from  'react' ; 
77
8+ import  type  {  CreditsPack  }  from  '@/app/api/help/credits/route' ; 
89import  type  {  SinglePrice  }  from  '@/app/api/help/prices/route' ; 
910
10- interface  PriceTableProps  { 
11+ type  PriceTableProps   =  { 
1112  prices : SinglePrice [ ] ; 
12- } 
13+   creditsPacks : CreditsPack [ ] ; 
14+ } ; 
1315
1416const  costUnitDictionary : Record < string ,  string >  =  { 
1517  creditsSimulation : 'credits / simulation' , 
@@ -51,7 +53,54 @@ function CustomHeaderCell({
5153  ) ; 
5254} 
5355
54- const  columns : ColumnsType < SinglePrice >  =  [ 
56+ const  creditsPackColumns : ColumnsType < CreditsPack >  =  [ 
57+   { 
58+     title : 'Credits' , 
59+     dataIndex : 'quantity' , 
60+     key : 'quantity' , 
61+     render : ( value : string )  =>  ( 
62+       < span  style = { {  fontWeight : 'bold' ,  color : '#002766'  } } > { value } </ span > 
63+     ) , 
64+   } , 
65+   { 
66+     title : 'Discount' , 
67+     dataIndex : 'discount' , 
68+     key : 'discount' , 
69+     render : ( value : number )  =>  { 
70+       if  ( ! value  ||  value  ===  0 )  { 
71+         return  < span  style = { {  color : '#002766'  } } > —</ span > ; 
72+       } 
73+       return  ( 
74+         < span  style = { {  color : '#002766'  } } > 
75+           < span  style = { {  fontWeight : 'normal'  } } > Save </ span > 
76+           < span  style = { {  fontWeight : 'bold'  } } > { value } </ span > %
77+         </ span > 
78+       ) ; 
79+     } , 
80+   } , 
81+   { 
82+     title : 'Price (CHF)' , 
83+     dataIndex : 'price' , 
84+     key : 'price' , 
85+     render : ( value : number )  =>  ( 
86+       < span  style = { {  color : '#002766'  } } > 
87+         < span  style = { {  fontWeight : 'bold'  } } > { value } </ span >  CHF
88+       </ span > 
89+     ) , 
90+   } , 
91+   { 
92+     title : 'Price/Credit (CHF)' , 
93+     dataIndex : 'pricePerCredit' , 
94+     key : 'pricePerCredit' , 
95+     render : ( value : number )  =>  ( 
96+       < span  style = { {  color : '#002766'  } } > 
97+         < span  style = { {  fontWeight : 'bold'  } } > { value } </ span >  CHF
98+       </ span > 
99+     ) , 
100+   } , 
101+ ] ; 
102+ 
103+ const  priceColumns : ColumnsType < SinglePrice >  =  [ 
55104  { 
56105    title : 'Item Name' , 
57106    dataIndex : 'itemName' , 
@@ -90,33 +139,114 @@ const columns: ColumnsType<SinglePrice> = [
90139  } , 
91140] ; 
92141
93- export  default  function  PriceTable ( {  prices } : PriceTableProps )  { 
142+ export  default  function  PriceTable ( {  prices,  creditsPacks  } : PriceTableProps )  { 
94143  const  sortedPrices  =  useMemo ( ( )  =>  { 
95144    return  [ ...prices ] . sort ( ( a ,  b )  =>  { 
145+       // Sort by freePrice (smallest to greatest), handling null values 
146+       const  priceA  =  a . freePrice  ??  Infinity ; 
147+       const  priceB  =  b . freePrice  ??  Infinity ; 
148+       if  ( priceA  !==  priceB )  { 
149+         return  priceA  -  priceB ; 
150+       } 
151+       // If prices are equal, sort by itemName alphabetically 
96152      const  nameA  =  ( a . itemName  ??  '' ) . toLowerCase ( ) ; 
97153      const  nameB  =  ( b . itemName  ??  '' ) . toLowerCase ( ) ; 
98154      return  nameA . localeCompare ( nameB ) ; 
99155    } ) ; 
100156  } ,  [ prices ] ) ; 
101157
102-   return  ( 
103-     < div > 
104-       < Table 
105-         dataSource = { sortedPrices } 
106-         columns = { columns } 
107-         rowKey = { ( record )  => 
108-           `${ record . itemName  ??  '' }  -${ record . freePrice  ??  '' }  -${ record . proPrice  ??  '' }  -${ record . costUnit  ??  '' }  ` 
158+   const  pricesBySection  =  useMemo ( ( )  =>  { 
159+     const  grouped : Record < string ,  SinglePrice [ ] >  =  { } ; 
160+     sortedPrices . forEach ( ( price )  =>  { 
161+       const  section  =  price . section  ||  'Other' ; 
162+       if  ( ! grouped [ section ] )  { 
163+         grouped [ section ]  =  [ ] ; 
164+       } 
165+       grouped [ section ] . push ( price ) ; 
166+     } ) ; 
167+     // Sort each section by freePrice (smallest to greatest) 
168+     Object . keys ( grouped ) . forEach ( ( section )  =>  { 
169+       grouped [ section ] . sort ( ( a ,  b )  =>  { 
170+         const  priceA  =  a . freePrice  ??  Infinity ; 
171+         const  priceB  =  b . freePrice  ??  Infinity ; 
172+         if  ( priceA  !==  priceB )  { 
173+           return  priceA  -  priceB ; 
109174        } 
110-         pagination = { false } 
111-         locale = { {  emptyText : 'No prices available'  } } 
112-         style = { {  fontSize : '16px' ,  color : '#002766' ,  backgroundColor : 'transparent'  } } 
113-         className = "[&_.ant-table]:bg-transparent [&_.ant-table-cell]:bg-transparent [&_.ant-table-cell]:text-[18px] [&_.ant-table-cell]:text-[#002766] [&_.ant-table-tbody>tr]:bg-transparent [&_.ant-table-tbody>tr>td]:bg-transparent [&_.ant-table-tbody>tr>td]:text-[18px] [&_.ant-table-thead>tr]:bg-transparent [&_.ant-table-thead>tr>th]:bg-transparent [&_.ant-table-thead>tr>th]:text-[16px] [&_.ant-table-thead>tr>th]:font-normal [&_.ant-table-thead>tr>th]:tracking-[0.025em] [&_.ant-table-thead>tr>th]:text-[#A5A5A5] [&_.ant-table-thead>tr>th]:uppercase" 
114-         components = { { 
115-           header : { 
116-             cell : CustomHeaderCell , 
117-           } , 
118-         } } 
119-       /> 
175+         // If prices are equal, sort by itemName alphabetically 
176+         const  nameA  =  ( a . itemName  ??  '' ) . toLowerCase ( ) ; 
177+         const  nameB  =  ( b . itemName  ??  '' ) . toLowerCase ( ) ; 
178+         return  nameA . localeCompare ( nameB ) ; 
179+       } ) ; 
180+     } ) ; 
181+     return  grouped ; 
182+   } ,  [ sortedPrices ] ) ; 
183+ 
184+   const  tableClassName  = 
185+     '[&_.ant-table]:bg-transparent [&_.ant-table-cell]:bg-transparent [&_.ant-table-cell]:text-[18px] [&_.ant-table-cell]:text-[#002766] [&_.ant-table-tbody>tr]:bg-transparent [&_.ant-table-tbody>tr>td]:bg-transparent [&_.ant-table-tbody>tr>td]:text-[18px] [&_.ant-table-thead>tr]:bg-transparent [&_.ant-table-thead>tr>th]:bg-transparent [&_.ant-table-thead>tr>th]:text-[16px] [&_.ant-table-thead>tr>th]:font-normal [&_.ant-table-thead>tr>th]:tracking-[0.025em] [&_.ant-table-thead>tr>th]:text-[#A5A5A5] [&_.ant-table-thead>tr>th]:uppercase' ; 
186+ 
187+   const  tableStyle  =  { 
188+     fontSize : '16px' , 
189+     color : '#002766' , 
190+     backgroundColor : 'transparent' , 
191+   }  as  const ; 
192+   const  tableComponents  =  { 
193+     header : { 
194+       cell : CustomHeaderCell , 
195+     } , 
196+   } ; 
197+ 
198+   const  sortedCreditsPacks  =  useMemo ( ( )  =>  { 
199+     return  [ ...creditsPacks ] . sort ( ( a ,  b )  =>  { 
200+       // Sort by discount (starting with no discount/0, then ascending) 
201+       const  discountA  =  a . discount  ??  0 ; 
202+       const  discountB  =  b . discount  ??  0 ; 
203+       return  discountA  -  discountB ; 
204+     } ) ; 
205+   } ,  [ creditsPacks ] ) ; 
206+ 
207+   return  ( 
208+     < div  className = "flex flex-col gap-8 overflow-auto" > 
209+       { /* Credits Packs Table */ } 
210+       { creditsPacks . length  >  0  &&  ( 
211+         < div > 
212+           < h3  className = "text-primary-8 mb-4 rounded-full bg-white/50 px-12 py-6 text-2xl font-bold" > 
213+             Credits
214+           </ h3 > 
215+           < Table 
216+             dataSource = { sortedCreditsPacks } 
217+             columns = { creditsPackColumns } 
218+             rowKey = { ( record ,  index )  =>  `credits-${ record . quantity }  -${ index }  ` } 
219+             pagination = { false } 
220+             locale = { {  emptyText : 'No credits packs available'  } } 
221+             style = { tableStyle } 
222+             className = { tableClassName } 
223+             components = { tableComponents } 
224+           /> 
225+         </ div > 
226+       ) } 
227+ 
228+       { /* Prices Tables by Section */ } 
229+       { Object . entries ( pricesBySection ) . map ( ( [ section ,  sectionPrices ] )  =>  ( 
230+         < div  key = { section } > 
231+           { section  !==  'Other'  &&  ( 
232+             < h3  className = "text-primary-8 mt-12 mb-4 rounded-full bg-white/50 px-12 py-6 text-2xl font-bold" > 
233+               { section } 
234+             </ h3 > 
235+           ) } 
236+           < Table 
237+             dataSource = { sectionPrices } 
238+             columns = { priceColumns } 
239+             rowKey = { ( record )  => 
240+               `${ record . itemName  ??  '' }  -${ record . freePrice  ??  '' }  -${ record . proPrice  ??  '' }  -${ record . costUnit  ??  '' }  ` 
241+             } 
242+             pagination = { false } 
243+             locale = { {  emptyText : 'No prices available'  } } 
244+             style = { tableStyle } 
245+             className = { tableClassName } 
246+             components = { tableComponents } 
247+           /> 
248+         </ div > 
249+       ) ) } 
120250    </ div > 
121251  ) ; 
122252} 
0 commit comments