11import { useCallback , useRef , useState } from 'react'
22import type { ChangeEvent , DragEvent , KeyboardEvent , MouseEvent } from 'react'
3- import './App.css'
3+
44import { UploadPage } from './components'
55import { UsersView } from './views/UsersView'
66import type { SeatOverrides } from './views/UsersView'
@@ -186,8 +186,13 @@ function App() {
186186 users : reportUsers ,
187187 } )
188188
189+ const sidebarItemBase = 'flex items-center gap-[10px] w-full px-3 py-[10px] border-0 rounded-md bg-transparent text-[13px] font-medium cursor-pointer text-left transition-colors hover:bg-bg-muted hover:text-fg-default disabled:opacity-40 disabled:cursor-default disabled:hover:bg-transparent disabled:hover:text-fg-muted focus-visible:outline-2 focus-visible:outline-app-accent focus-visible:outline-offset-[-2px] max-sm:justify-center max-sm:p-2'
190+ const sidebarActive = 'bg-app-accent-subtle text-app-accent font-semibold hover:bg-app-accent-muted'
191+ const sidebarInactive = 'text-fg-muted'
192+ const viewContentClasses = 'max-w-[var(--width-content-max)] w-full mx-auto px-6 pt-8 pb-12 flex flex-col gap-6 min-[1440px]:-translate-x-[calc(var(--width-sidebar)/2)]'
193+
189194 return (
190- < div className = { `app ${ hasReport ? 'app--review ' : '' } ` } >
195+ < div className = { `min-h-screen flex flex-col ${ hasReport ? 'bg-bg-muted ' : '' } ` } >
191196 < input
192197 ref = { fileInputRef }
193198 id = "file-input"
@@ -198,16 +203,16 @@ function App() {
198203 style = { { display : 'none' } }
199204 />
200205
201- < header className = "site -header" >
202- < div className = "site-header__left " >
203- < svg className = "site-header__logo " height = "32" viewBox = "0 0 16 16" width = "32" fill = "#fff" aria-hidden = "true" >
206+ < header className = "bg -header-bg py-[14px] px-6 flex justify-between items-center gap-3 flex-wrap max-sm:px-4 max-sm:py-3 " >
207+ < div className = "flex items-center gap-3 " >
208+ < svg className = "block " height = "32" viewBox = "0 0 16 16" width = "32" fill = "#fff" aria-hidden = "true" >
204209 < path d = "M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z" />
205210 </ svg >
206- < span className = "site-header__title " > Billing Preview</ span >
211+ < span className = "text-lg font-semibold text-white tracking-tight max-sm:text-xs " > Billing Preview</ span >
207212 </ div >
208213 { hasReport && (
209- < div className = "site-header__right " >
210- < button className = "site-header__button " onClick = { triggerFileDialog } >
214+ < div className = "flex items-center max-sm:w-full " >
215+ < button className = "border border-border-emphasis rounded-md bg-transparent text-white px-4 py-2 text-sm font-medium cursor-pointer transition-colors whitespace-nowrap hover:bg-white/[0.08] hover:border-border-emphasis focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2 max-sm:w-full max-sm:text-center " onClick = { triggerFileDialog } >
211216 Upload another report
212217 </ button >
213218 </ div >
@@ -216,144 +221,144 @@ function App() {
216221
217222 { hasReport ? (
218223 < >
219- < nav className = "secondary-header " >
220- < div className = "secondary-header__info " >
221- < span className = "secondary-header__label " > File:</ span >
222- < span className = "secondary-header__value " > { fileName ?? 'Processing…' } </ span >
224+ < nav className = "bg-bg-default border-b border-border-default px-6 py-3 flex justify-between items-center gap-4 flex-wrap max-sm:px-4 max-sm:flex-col max-sm:items-start max-sm:gap-3 " >
225+ < div className = "flex items-center gap-2 flex-wrap text-sm text-fg-default max-sm:flex-col max-sm:items-start max-sm:gap-1 " >
226+ < span className = "text-fg-muted font-medium " > File:</ span >
227+ < span className = "font-semibold text-fg-default " > { fileName ?? 'Processing…' } </ span >
223228 { reportContext && ( reportContext . startDate || reportContext . endDate ) && (
224229 < >
225- < span className = "secondary-header__separator " > |</ span >
226- < span className = "secondary-header__label " > Report window:</ span >
227- < span className = "secondary-header__value " >
230+ < span className = "text-border-default mx-1 max-sm:hidden " > |</ span >
231+ < span className = "text-fg-muted font-medium " > Report window:</ span >
232+ < span className = "font-semibold text-fg-default " >
228233 { reportContext . startDate ?? '—' } to { reportContext . endDate ?? '—' }
229234 </ span >
230235 </ >
231236 ) }
232237 { quickStats && (
233238 < >
234- < span className = "secondary-header__separator " > |</ span >
235- < span className = "secondary-header__label " > Total rows:</ span >
236- < span className = "secondary-header__value " > { quickStats . lineCount . toLocaleString ( ) } </ span >
239+ < span className = "text-border-default mx-1 max-sm:hidden " > |</ span >
240+ < span className = "text-fg-muted font-medium " > Total rows:</ span >
241+ < span className = "font-semibold text-fg-default " > { quickStats . lineCount . toLocaleString ( ) } </ span >
237242 </ >
238243 ) }
239244 </ div >
240245 </ nav >
241246
242- < div className = "app-layout " >
243- < aside className = "sidebar" aria-label = "Navigation" >
244- < nav className = "sidebar__nav " >
247+ < div className = "flex flex-1 min-h-0 " >
248+ < aside className = "w-[var(--width- sidebar)] shrink-0 p-4 pr-0 sticky top-0 self-start max-h-screen overflow-y-auto max-sm:w-12 max-sm:pl-1 " aria-label = "Navigation" >
249+ < nav className = "bg-bg-default border border-border-default rounded-lg p-[6px] flex flex-col gap-[2px] max-sm:border-0 max-sm:p-[2px] " >
245250 < button
246251 type = "button"
247- className = { `sidebar__item ${ activeView === 'overview' ? 'sidebar__item--active' : '' } ` }
252+ className = { `${ sidebarItemBase } ${ activeView === 'overview' ? sidebarActive : sidebarInactive } ` }
248253 onClick = { ( ) => setActiveView ( 'overview' ) }
249254 >
250- < svg className = "sidebar__icon " viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" aria-hidden = "true" focusable = "false" >
255+ < svg className = "w-[18px] h-[18px] shrink-0 " viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" aria-hidden = "true" focusable = "false" >
251256 < rect x = "3" y = "3" width = "7" height = "7" rx = "1" />
252257 < rect x = "14" y = "3" width = "7" height = "7" rx = "1" />
253258 < rect x = "3" y = "14" width = "7" height = "7" rx = "1" />
254259 < rect x = "14" y = "14" width = "7" height = "7" rx = "1" />
255260 </ svg >
256- < span className = "sidebar__label " > Overview</ span >
261+ < span className = "whitespace-nowrap overflow-hidden text-ellipsis max-sm:hidden " > Overview</ span >
257262 </ button >
258263
259264 < button
260265 type = "button"
261- className = { `sidebar__item ${ userNavActive ? 'sidebar__item--active' : '' } ` }
266+ className = { `${ sidebarItemBase } ${ userNavActive ? sidebarActive : sidebarInactive } ` }
262267 disabled = { ! userUsage }
263268 onClick = { openUserView }
264269 >
265- < svg className = "sidebar__icon " viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" aria-hidden = "true" focusable = "false" >
270+ < svg className = "w-[18px] h-[18px] shrink-0 " viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" aria-hidden = "true" focusable = "false" >
266271 < circle cx = "12" cy = "8" r = "4" />
267272 < path d = "M20 21a8 8 0 1 0-16 0" />
268273 </ svg >
269- < span className = "sidebar__label " > { isIndividualReport ? 'User' : 'Users' } </ span >
274+ < span className = "whitespace-nowrap overflow-hidden text-ellipsis max-sm:hidden " > { isIndividualReport ? 'User' : 'Users' } </ span >
270275 </ button >
271276
272277 { modelUsage && modelUsage . models . length > 0 && (
273278 < button
274279 type = "button"
275- className = { `sidebar__item ${ activeView === 'models' ? 'sidebar__item--active' : '' } ` }
280+ className = { `${ sidebarItemBase } ${ activeView === 'models' ? sidebarActive : sidebarInactive } ` }
276281 onClick = { ( ) => setActiveView ( 'models' ) }
277282 >
278- < svg className = "sidebar__icon " viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" aria-hidden = "true" focusable = "false" >
283+ < svg className = "w-[18px] h-[18px] shrink-0 " viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" aria-hidden = "true" focusable = "false" >
279284 < path d = "M12 2L2 7l10 5 10-5-10-5Z" />
280285 < path d = "M2 17l10 5 10-5" />
281286 < path d = "M2 12l10 5 10-5" />
282287 </ svg >
283- < span className = "sidebar__label " > Models</ span >
288+ < span className = "whitespace-nowrap overflow-hidden text-ellipsis max-sm:hidden " > Models</ span >
284289 </ button >
285290 ) }
286291
287292 < button
288293 type = "button"
289- className = { `sidebar__item ${ activeView === 'products' ? 'sidebar__item--active' : '' } ` }
294+ className = { `${ sidebarItemBase } ${ activeView === 'products' ? sidebarActive : sidebarInactive } ` }
290295 onClick = { ( ) => setActiveView ( 'products' ) }
291296 >
292- < svg className = "sidebar__icon " viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" aria-hidden = "true" focusable = "false" >
297+ < svg className = "w-[18px] h-[18px] shrink-0 " viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" aria-hidden = "true" focusable = "false" >
293298 < path d = "M3 9h18M3 15h18M9 3v18M15 3v18" />
294299 </ svg >
295- < span className = "sidebar__label " > Products</ span >
300+ < span className = "whitespace-nowrap overflow-hidden text-ellipsis max-sm:hidden " > Products</ span >
296301 </ button >
297302
298303 { orgs && orgs . organizations . length > 0 && (
299304 < button
300305 type = "button"
301- className = { `sidebar__item ${ activeView === 'orgs' ? 'sidebar__item--active' : '' } ` }
306+ className = { `${ sidebarItemBase } ${ activeView === 'orgs' ? sidebarActive : sidebarInactive } ` }
302307 onClick = { ( ) => setActiveView ( 'orgs' ) }
303308 >
304- < svg className = "sidebar__icon " viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" aria-hidden = "true" focusable = "false" >
309+ < svg className = "w-[18px] h-[18px] shrink-0 " viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" aria-hidden = "true" focusable = "false" >
305310 < rect x = "4" y = "3" width = "16" height = "18" rx = "1" />
306311 < path d = "M9 7h2M9 11h2M9 15h2M13 7h2M13 11h2M13 15h2" />
307312 </ svg >
308- < span className = "sidebar__label " > Organizations</ span >
313+ < span className = "whitespace-nowrap overflow-hidden text-ellipsis max-sm:hidden " > Organizations</ span >
309314 </ button >
310315 ) }
311316
312317 { costCenters && costCenters . costCenters . length > 0 && (
313318 < button
314319 type = "button"
315- className = { `sidebar__item ${ activeView === 'costCenters' ? 'sidebar__item--active' : '' } ` }
320+ className = { `${ sidebarItemBase } ${ activeView === 'costCenters' ? sidebarActive : sidebarInactive } ` }
316321 onClick = { ( ) => setActiveView ( 'costCenters' ) }
317322 >
318- < svg className = "sidebar__icon " viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" aria-hidden = "true" focusable = "false" >
323+ < svg className = "w-[18px] h-[18px] shrink-0 " viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" aria-hidden = "true" focusable = "false" >
319324 < path d = "M12 3C8.5 3 6 5 6 7c0 2 2.5 4 6 4s6-2 6-4c0-2-2.5-4-6-4Z" />
320325 < path d = "M6 7v4c0 2 2.5 4 6 4s6-2 6-4V7" />
321326 < path d = "M6 11v4c0 2 2.5 4 6 4s6-2 6-4v-4" />
322327 </ svg >
323- < span className = "sidebar__label " > Cost Centers</ span >
328+ < span className = "whitespace-nowrap overflow-hidden text-ellipsis max-sm:hidden " > Cost Centers</ span >
324329 </ button >
325330 ) }
326331
327- < hr className = "sidebar__divider " />
332+ < hr className = "border-0 border-t border-border-default my-[6px] " />
328333
329334 < button
330335 type = "button"
331- className = { `sidebar__item ${ activeView === 'guide' ? 'sidebar__item--active' : '' } ` }
336+ className = { `${ sidebarItemBase } ${ activeView === 'guide' ? sidebarActive : sidebarInactive } ` }
332337 onClick = { ( ) => setActiveView ( 'guide' ) }
333338 >
334- < svg className = "sidebar__icon " viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" aria-hidden = "true" focusable = "false" >
339+ < svg className = "w-[18px] h-[18px] shrink-0 " viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" aria-hidden = "true" focusable = "false" >
335340 < circle cx = "12" cy = "12" r = "10" />
336341 < path d = "M12 16v-4M12 8h.01" />
337342 </ svg >
338- < span className = "sidebar__label " > Report Format</ span >
343+ < span className = "whitespace-nowrap overflow-hidden text-ellipsis max-sm:hidden " > Report Format</ span >
339344 </ button >
340345
341346 < button
342347 type = "button"
343- className = { `sidebar__item ${ activeView === 'faq' ? 'sidebar__item--active' : '' } ` }
348+ className = { `${ sidebarItemBase } ${ activeView === 'faq' ? sidebarActive : sidebarInactive } ` }
344349 onClick = { ( ) => setActiveView ( 'faq' ) }
345350 >
346- < svg className = "sidebar__icon " viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" aria-hidden = "true" focusable = "false" >
351+ < svg className = "w-[18px] h-[18px] shrink-0 " viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" aria-hidden = "true" focusable = "false" >
347352 < circle cx = "12" cy = "12" r = "10" />
348353 < path d = "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" />
349354 < circle cx = "12" cy = "17" r = "0.5" fill = "currentColor" />
350355 </ svg >
351- < span className = "sidebar__label " > FAQ</ span >
356+ < span className = "whitespace-nowrap overflow-hidden text-ellipsis max-sm:hidden " > FAQ</ span >
352357 </ button >
353358 </ nav >
354359 </ aside >
355360
356- < main className = "report-content " >
361+ < main className = "flex-1 min-w-0 flex flex-col " >
357362 { activeView === 'overview' ? (
358363 < OverviewView
359364 error = { error }
@@ -364,7 +369,7 @@ function App() {
364369 />
365370 ) : activeView === 'models' ? (
366371 modelUsage && modelUsage . models . length > 0 ? (
367- < div className = "view-content" >
372+ < div className = { viewContentClasses } >
368373 < ModelsView
369374 modelUsage = { modelUsage }
370375 rangeStart = { rangeStart }
@@ -373,7 +378,7 @@ function App() {
373378 </ div >
374379 ) : null
375380 ) : activeView === 'users' && ! isIndividualReport ? (
376- < div className = "view-content" >
381+ < div className = { viewContentClasses } >
377382 < UsersView
378383 users = { reportUsers }
379384 costOptimizationOpportunity = { costOptimizationOpportunity }
@@ -386,7 +391,7 @@ function App() {
386391 />
387392 </ div >
388393 ) : activeView === 'userDetails' || ( activeView === 'users' && isIndividualReport ) ? (
389- < div className = "view-content" >
394+ < div className = { viewContentClasses } >
390395 < UserDetailsView
391396 user = { selectedUser }
392397 reportPlanScope = { reportPlanScope }
@@ -398,23 +403,23 @@ function App() {
398403 />
399404 </ div >
400405 ) : activeView === 'costCenters' ? (
401- < div className = "view-content" >
406+ < div className = { viewContentClasses } >
402407 < CostCentersView data = { costCenters ?? { costCenters : [ ] } } rangeStart = { rangeStart } />
403408 </ div >
404409 ) : activeView === 'products' ? (
405- < div className = "view-content" >
410+ < div className = { viewContentClasses } >
406411 < ProductsView data = { productUsage ?? { products : [ ] } } />
407412 </ div >
408413 ) : activeView === 'guide' ? (
409- < div className = "view-content" >
414+ < div className = { viewContentClasses } >
410415 < ReportGuideView />
411416 </ div >
412417 ) : activeView === 'faq' ? (
413- < div className = "view-content" >
418+ < div className = { viewContentClasses } >
414419 < FaqView />
415420 </ div >
416421 ) : (
417- < div className = "view-content" >
422+ < div className = { viewContentClasses } >
418423 < OrganizationsView data = { orgs ?? { organizations : [ ] } } rangeStart = { rangeStart } />
419424 </ div >
420425 ) }
@@ -437,7 +442,7 @@ function App() {
437442 ) }
438443
439444 { hasReport && (
440- < footer className = "site-footer " >
445+ < footer className = "text-center text-fg-muted text-xs leading-[1.6] pt-6 px-4 pb-10 max-w-[960px] mx-auto w-full [&_a]:text-fg-muted [&_a]:no-underline [&_a:hover]:underline " >
441446 This is a preview based on your uploaded usage data. Actual bills may differ.< br />
442447 Your data never leaves your browser.{ ' ' }
443448 Questions? < a href = "https://support.github.com" target = "_blank" rel = "noopener noreferrer" > Contact GitHub Support</ a > .
0 commit comments