@@ -22,12 +22,27 @@ import useConfiguration from '../../hooks/use-configuration';
2222
2323const logger = new Logger ( 'DocumentPanel' ) ;
2424
25- // Format the cost cell content based on whether it's a total row
26- const formatCostCell = ( item ) => {
27- if ( item . isTotal ) {
28- return < Box fontWeight = "bold" > { `${ item . note } : ${ item . cost } ` } </ Box > ;
25+ // Helper function to parse serviceApi key into context and service
26+ const parseServiceApiKey = ( serviceApiKey ) => {
27+ const parts = serviceApiKey . split ( '/' ) ;
28+ if ( parts . length >= 3 ) {
29+ const context = parts [ 0 ] ;
30+ const serviceApi = parts . slice ( 1 ) . join ( '/' ) ;
31+ return { context, serviceApi } ;
2932 }
30- return item . cost ;
33+ // Fallback for keys that don't follow the new format (less than 3 parts) - set context to ''
34+ return { context : '' , serviceApi : serviceApiKey } ;
35+ } ;
36+
37+ // Helper function to format cost cells
38+ const formatCostCell = ( rowItem ) => {
39+ if ( rowItem . isTotal ) {
40+ return < Box fontWeight = "bold" > { `${ rowItem . note } : ${ rowItem . cost } ` } </ Box > ;
41+ }
42+ if ( rowItem . isSubtotal ) {
43+ return < Box fontWeight = "bold" color = "text-body-secondary" > { `${ rowItem . note } : ${ rowItem . cost } ` } </ Box > ;
44+ }
45+ return rowItem . cost ;
3146} ;
3247
3348// Component to display metering information in a table
@@ -65,15 +80,18 @@ const MeteringTable = ({ meteringData, preCalculatedTotals }) => {
6580 return < Box > Loading pricing data...</ Box > ;
6681 }
6782
68- // Transform metering data into table rows
69- const tableItems = [ ] ;
83+ // Transform metering data into table rows with context parsing
84+ const rawTableItems = [ ] ;
85+ const contextTotals = { } ;
7086 let totalCost = 0 ;
7187
72- Object . entries ( meteringData ) . forEach ( ( [ serviceApi , metrics ] ) => {
88+ Object . entries ( meteringData ) . forEach ( ( [ originalServiceApiKey , metrics ] ) => {
89+ const { context, serviceApi } = parseServiceApiKey ( originalServiceApiKey ) ;
90+
7391 Object . entries ( metrics ) . forEach ( ( [ unit , value ] ) => {
7492 const numericValue = Number ( value ) ;
7593
76- // Look up the unit price from the pricing data
94+ // Look up the unit price from the pricing data using the parsed serviceApi
7795 let unitPrice = null ;
7896 let unitPriceDisplayValue = 'None' ;
7997 let cost = 0 ;
@@ -83,6 +101,13 @@ const MeteringTable = ({ meteringData, preCalculatedTotals }) => {
83101 unitPriceDisplayValue = `$${ unitPrice } ` ;
84102 cost = numericValue * unitPrice ;
85103 totalCost += cost ;
104+
105+ // Track context totals
106+ if ( ! contextTotals [ context ] ) {
107+ contextTotals [ context ] = 0 ;
108+ }
109+ contextTotals [ context ] += cost ;
110+
86111 logger . debug ( `Found price for ${ serviceApi } /${ unit } : ${ unitPriceDisplayValue } ` ) ;
87112 } else {
88113 logger . warn ( `Invalid price for ${ serviceApi } /${ unit } , using None` ) ;
@@ -91,34 +116,80 @@ const MeteringTable = ({ meteringData, preCalculatedTotals }) => {
91116 logger . debug ( `No price found for ${ serviceApi } /${ unit } , using None` ) ;
92117 }
93118
94- tableItems . push ( {
119+ rawTableItems . push ( {
120+ context,
95121 serviceApi,
96122 unit,
97123 value : String ( numericValue ) ,
98124 unitCost : unitPriceDisplayValue ,
99125 cost : unitPrice !== null ? `$${ cost . toFixed ( 4 ) } ` : 'N/A' ,
126+ costValue : cost ,
100127 isTotal : false ,
128+ isSubtotal : false ,
101129 } ) ;
102130 } ) ;
103131 } ) ;
104132
133+ // Group items by context and add subtotals
134+ const tableItems = [ ] ;
135+ const contextGroups = { } ;
136+
137+ // Group raw items by context
138+ rawTableItems . forEach ( ( item ) => {
139+ if ( ! contextGroups [ item . context ] ) {
140+ contextGroups [ item . context ] = [ ] ;
141+ }
142+ contextGroups [ item . context ] . push ( item ) ;
143+ } ) ;
144+
145+ // Sort contexts alphabetically and add items with subtotals
146+ const sortedContexts = Object . keys ( contextGroups ) . sort ( ) ;
147+
148+ sortedContexts . forEach ( ( context ) => {
149+ // Add all items for this context
150+ tableItems . push ( ...contextGroups [ context ] ) ;
151+
152+ // Add subtotal row for this context
153+ const contextTotal = contextTotals [ context ] || 0 ;
154+ tableItems . push ( {
155+ context : '' ,
156+ serviceApi : '' ,
157+ unit : '' ,
158+ value : '' ,
159+ unitCost : '' ,
160+ cost : `$${ contextTotal . toFixed ( 4 ) } ` ,
161+ costValue : contextTotal ,
162+ isTotal : false ,
163+ isSubtotal : true ,
164+ note : `${ context } Subtotal` ,
165+ } ) ;
166+ } ) ;
167+
105168 // Use preCalculatedTotals if provided, otherwise calculate locally
106169 const finalTotalCost = preCalculatedTotals ? preCalculatedTotals . totalCost : totalCost ;
107170
108- // Add total row
171+ // Add overall total row
109172 tableItems . push ( {
173+ context : '' ,
110174 serviceApi : '' ,
111175 unit : '' ,
112176 value : '' ,
113177 unitCost : '' ,
114178 cost : `$${ finalTotalCost . toFixed ( 4 ) } ` ,
179+ costValue : finalTotalCost ,
115180 isTotal : true ,
181+ isSubtotal : false ,
116182 note : 'Total' ,
117183 } ) ;
118184
119185 return (
120186 < Table
121187 columnDefinitions = { [
188+ {
189+ id : 'context' ,
190+ header : 'Context' ,
191+ cell : ( rowItem ) => rowItem . context ,
192+ } ,
122193 {
123194 id : 'serviceApi' ,
124195 header : 'Service/Api' ,
@@ -169,7 +240,10 @@ const calculateTotalCosts = (meteringData, documentItem, pricingData) => {
169240 let totalCost = 0 ;
170241
171242 if ( pricingData ) {
172- Object . entries ( meteringData ) . forEach ( ( [ serviceApi , metrics ] ) => {
243+ Object . entries ( meteringData ) . forEach ( ( [ originalServiceApiKey , metrics ] ) => {
244+ // Parse the serviceApi key to remove context prefix
245+ const { serviceApi } = parseServiceApiKey ( originalServiceApiKey ) ;
246+
173247 Object . entries ( metrics ) . forEach ( ( [ unit , value ] ) => {
174248 const numericValue = Number ( value ) ;
175249 if ( pricingData [ serviceApi ] && pricingData [ serviceApi ] [ unit ] !== undefined ) {
0 commit comments