@@ -3,6 +3,7 @@ import { BillingProjectionDisclaimer, BillingTotalsCards } from '../components/u
33import { appLinks } from '../config/links'
44import type { ReportPlanScope } from '../pipeline/aicIncludedCredits'
55import type { DailyUsageData } from '../pipeline/aggregators/dailyUsageAggregator'
6+ import { AIC_UNIT_PRICE_USD } from '../utils/billingConstants'
67import { fillDataForRange } from '../utils/fillDataForRange'
78import { formatUsd } from '../utils/format'
89import type { IndividualPlanUpgradeRecommendation } from '../utils/individualPlanUpgrade'
@@ -18,11 +19,16 @@ type OverviewViewProps = {
1819 business : number
1920 enterprise : number
2021 }
22+ includedAicCredits : number
2123 reportPlanScope ?: ReportPlanScope
2224 upgradeRecommendation ?: IndividualPlanUpgradeRecommendation | null
2325 onAdjustSeatCounts ?: ( ) => void
2426}
2527
28+ const CURRENT_AIC_COLOR = '#1a7f37'
29+ const ADDITIONAL_AIC_COLOR = '#cf222e'
30+ const INCLUDED_CREDITS_COLOR = '#0969da'
31+
2632function createEmptyDailyUsage ( date : string ) : DailyUsageData {
2733 return {
2834 date,
@@ -44,6 +50,7 @@ export function OverviewView({
4450 rangeEnd,
4551 licenseAmount,
4652 licenseSeatCounts,
53+ includedAicCredits,
4754 reportPlanScope = 'organization' ,
4855 upgradeRecommendation = null ,
4956 onAdjustSeatCounts,
@@ -73,6 +80,23 @@ export function OverviewView({
7380 const usageBasedBillingDocsUrl = reportPlanScope === 'individual'
7481 ? appLinks . usageBasedBillingForIndividualsDocs
7582 : appLinks . usageBasedBillingForOrganizationsDocs
83+ const includedCreditsValue = includedAicCredits * AIC_UNIT_PRICE_USD
84+ const includedCreditsLabel = 'Included value'
85+ const includedCreditsLegendLabel = reportPlanScope === 'individual' ? 'Included AI Credits' : 'Included AI Credits pool'
86+ const includedCreditsDescription = reportPlanScope === 'individual'
87+ ? 'Your plan\'s included AI Credits are consumed first. Additional usage spend starts after cumulative AIC gross cost exceeds the included value.'
88+ : 'The account-wide included AI Credits pool is consumed first. Additional usage spend starts after cumulative AIC gross cost exceeds the included value.'
89+ const includedCreditsCardTitle = reportPlanScope === 'individual' ? 'Included credits are coming' : 'Pooled included credits are coming'
90+ const includedCreditsCardBody = reportPlanScope === 'individual'
91+ ? 'Under usage-based billing, your Copilot plan includes AI Credits each month. Usage consumes those included credits first; additional usage is billed only after they are used.'
92+ : 'Under usage-based billing, included credits will be pooled across all licensed users in your account. No more unused capacity going to waste from idle users.'
93+ const includedCreditsDocsUrl = reportPlanScope === 'individual'
94+ ? appLinks . usageBasedBillingForIndividualsDocs
95+ : appLinks . aiCreditsForOrganizationsDocs
96+ const cumulativeAicGrossAmount = filledDailyUsageData . reduce < number [ ] > ( ( totals , day ) => {
97+ totals . push ( ( totals [ totals . length - 1 ] ?? 0 ) + day . aicGrossAmount )
98+ return totals
99+ } , [ ] )
76100
77101 return (
78102 < div className = "max-w-[var(--width-content-max)] w-full mx-auto px-6 pt-8 pb-12 flex flex-col gap-6" >
@@ -146,25 +170,51 @@ export function OverviewView({
146170 < BillingProjectionDisclaimer className = "mb-6" />
147171
148172 < section className = "grid grid-cols-1 gap-6 w-full" >
149- < DualAxisLineChart
150- title = "Daily Requests & AI Credits"
151- labels = { filledDailyUsageData . map ( ( day ) => day . date ) }
152- series = { [
153- {
154- label : 'Premium Requests' ,
155- color : '#6366f1' ,
156- data : filledDailyUsageData . map ( ( day ) => day . requests ) ,
157- yAxisID : 'y' ,
158- } ,
159- {
160- label : 'AI Credits' ,
161- color : '#22c55e' ,
162- data : filledDailyUsageData . map ( ( day ) => day . aicQuantity ) ,
163- yAxisID : 'y1' ,
164- } ,
165- ] }
166- height = { 320 }
167- />
173+ < div className = "flex flex-col gap-2" >
174+ < DualAxisLineChart
175+ title = "Cumulative AIC gross cost: included vs additional"
176+ labels = { filledDailyUsageData . map ( ( day ) => day . date ) }
177+ series = { [
178+ {
179+ label : 'AIC gross cost' ,
180+ legendLabel : 'Usage - within included value' ,
181+ legendOrder : 1 ,
182+ color : CURRENT_AIC_COLOR ,
183+ data : cumulativeAicGrossAmount ,
184+ yAxisID : 'y' ,
185+ order : 1 ,
186+ segmentColor : ( _startValue , endValue ) => (
187+ endValue <= includedCreditsValue
188+ ? CURRENT_AIC_COLOR
189+ : ADDITIONAL_AIC_COLOR
190+ ) ,
191+ } ,
192+ {
193+ label : includedCreditsLabel ,
194+ legendLabel : includedCreditsLegendLabel ,
195+ legendOrder : 3 ,
196+ color : INCLUDED_CREDITS_COLOR ,
197+ data : filledDailyUsageData . map ( ( ) => includedCreditsValue ) ,
198+ yAxisID : 'y' ,
199+ borderDash : [ 2 , 4 ] ,
200+ order : 2 ,
201+ pointRadius : 0 ,
202+ } ,
203+ ] }
204+ extraLegendItems = { [
205+ {
206+ label : 'Usage - additional spend' ,
207+ color : ADDITIONAL_AIC_COLOR ,
208+ legendOrder : 2 ,
209+ } ,
210+ ] }
211+ formatYAsCurrency
212+ height = { 320 }
213+ />
214+ < p className = "m-0 text-center text-[13px] text-fg-muted leading-normal" >
215+ { includedCreditsDescription }
216+ </ p >
217+ </ div >
168218 < DualAxisLineChart
169219 title = "Daily cost: PRU cost vs AIC cost"
170220 labels = { filledDailyUsageData . map ( ( day ) => day . date ) }
@@ -213,24 +263,22 @@ export function OverviewView({
213263 />
214264 </ section >
215265
216- { reportPlanScope === 'organization' && (
217- < div className = "bg-bg-default border border-border-default rounded-md py-5 px-6 mt-6 flex flex-col gap-3 sm:flex-row sm:items-center sm:gap-5" >
218- < div className = "flex-1 flex flex-col gap-1" >
219- < strong className = "text-sm font-semibold text-fg-default" > Pooled included credits are coming</ strong >
220- < p className = "m-0 text-[13px] text-fg-muted leading-normal" >
221- Under usage-based billing, included credits will be pooled across all licensed users in your account. No more unused capacity going to waste from idle users.
222- </ p >
223- </ div >
224- < a
225- href = { appLinks . aiCreditsForOrganizationsDocs }
226- className = "text-sm font-medium text-fg-accent no-underline whitespace-nowrap hover:underline"
227- target = "_blank"
228- rel = "noopener noreferrer"
229- >
230- Learn more →
231- </ a >
266+ < div className = "bg-bg-default border border-border-default rounded-md py-5 px-6 mt-6 flex flex-col gap-3 sm:flex-row sm:items-center sm:gap-5" >
267+ < div className = "flex-1 flex flex-col gap-1" >
268+ < strong className = "text-sm font-semibold text-fg-default" > { includedCreditsCardTitle } </ strong >
269+ < p className = "m-0 text-[13px] text-fg-muted leading-normal" >
270+ { includedCreditsCardBody }
271+ </ p >
232272 </ div >
233- ) }
273+ < a
274+ href = { includedCreditsDocsUrl }
275+ className = "text-sm font-medium text-fg-accent no-underline whitespace-nowrap hover:underline"
276+ target = "_blank"
277+ rel = "noopener noreferrer"
278+ >
279+ Learn more →
280+ </ a >
281+ </ div >
234282
235283 < section className = "mt-8" >
236284 < h2 className = "text-base font-semibold text-fg-default pb-[10px] border-b border-border-default mb-4" > Recommended next steps</ h2 >
0 commit comments