@@ -4,6 +4,7 @@ import { DesignCategoryTabs, DesignEditableGrid, DesignMenu, type DesignCategory
44import { EditableInput } from "@/components/editable-input" ;
55import { FormDialog , SmartFormDialog } from "@/components/form-dialog" ;
66import { InputField , SelectField } from "@/components/form-fields" ;
7+ import { Link } from "@/components/link" ;
78import { MetadataSection } from "@/components/metadata-editor" ;
89import {
910 Accordion ,
@@ -33,13 +34,13 @@ import {
3334 Typography ,
3435 useToast
3536} from "@/components/ui" ;
36- import { Link } from "@/components/link" ;
3737import { DeleteUserDialog , ImpersonateUserDialog } from "@/components/user-dialogs" ;
3838import { ALL_APPS_FRONTEND } from "@/lib/apps-frontend" ;
3939import { isAppEnabled } from "@/lib/apps-utils" ;
4040import { parseRiskScore } from "@/lib/risk-score-utils" ;
4141import { useUserActivityOrThrow } from "@/lib/stack-app-internals" ;
4242import { AtIcon , CalendarIcon , CheckIcon , DatabaseIcon , EnvelopeIcon , GlobeIcon , HashIcon , PlusIcon , ProhibitIcon , ShieldIcon , SquareIcon , XIcon } from "@phosphor-icons/react" ;
43+ import { type DataGridColumnDef } from "@stackframe/dashboard-ui-components" ;
4344import { ServerContactChannel , ServerOAuthProvider , ServerTeam , ServerUser } from "@stackframe/stack" ;
4445import { KnownErrors } from "@stackframe/stack-shared" ;
4546import { AppId } from "@stackframe/stack-shared/dist/apps/apps-config" ;
@@ -48,7 +49,6 @@ import { fromNow } from "@stackframe/stack-shared/dist/utils/dates";
4849import { captureError , StackAssertionError , throwErr } from '@stackframe/stack-shared/dist/utils/errors' ;
4950import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises" ;
5051import { deindent } from "@stackframe/stack-shared/dist/utils/strings" ;
51- import { type DataGridColumnDef } from "@stackframe/dashboard-ui-components" ;
5252import { Suspense , useCallback , useMemo , useState , type ReactNode } from "react" ;
5353import * as yup from "yup" ;
5454import { AppEnabledGuard } from "../../app-enabled-guard" ;
@@ -111,7 +111,7 @@ function UserHeader({ user }: UserHeaderProps) {
111111 } } />
112112 < p > Last active { fromNow ( user . lastActiveAt ) } </ p >
113113 </ div >
114- < div >
114+ < div className = "shrink-0 mr-8" >
115115 < DesignMenu
116116 variant = "actions"
117117 trigger = "icon"
@@ -1340,13 +1340,13 @@ const ACTIVITY_GRID_CELLS = ACTIVITY_GRID_COLUMNS * ACTIVITY_GRID_ROWS;
13401340const ACTIVITY_CELL_SIZE_PX = 8 ;
13411341const ACTIVITY_GRID_GAP_PX = 2 ;
13421342const ACTIVITY_WEEKDAY_LABELS = [
1343+ { label : "" , ariaLabel : null } ,
13431344 { label : "M" , ariaLabel : "Monday" } ,
13441345 { label : "" , ariaLabel : null } ,
13451346 { label : "W" , ariaLabel : "Wednesday" } ,
13461347 { label : "" , ariaLabel : null } ,
13471348 { label : "F" , ariaLabel : "Friday" } ,
13481349 { label : "" , ariaLabel : null } ,
1349- { label : "S" , ariaLabel : "Sunday" } ,
13501350] as const ;
13511351
13521352// Activity heatmap color ramp. Indexed by 0 = no activity, 1..4 = increasing
@@ -1403,17 +1403,17 @@ function formatActivityIsoDate(date: Date): string {
14031403 return date . toISOString ( ) . slice ( 0 , 10 ) ;
14041404}
14051405
1406- function getMondayWeekStart ( date : Date ) : Date {
1406+ function getSundayWeekStart ( date : Date ) : Date {
14071407 const weekStart = new Date ( date ) ;
14081408 weekStart . setUTCHours ( 0 , 0 , 0 , 0 ) ;
1409- const dayIndex = ( weekStart . getUTCDay ( ) + 6 ) % 7 ;
1409+ const dayIndex = weekStart . getUTCDay ( ) ;
14101410 weekStart . setUTCDate ( weekStart . getUTCDate ( ) - dayIndex ) ;
14111411 return weekStart ;
14121412}
14131413
14141414function ActivityShell ( { children } : { children : ReactNode } ) {
14151415 return (
1416- < div className = "hidden xl:flex flex-col items-end gap-1.5 shrink-0 pt-1" >
1416+ < div className = "hidden xl:flex flex-col items-center gap-1.5 shrink-0 pt-1" >
14171417 < span className = "text-[11px] font-medium text-muted-foreground tracking-wide uppercase" > Activity</ span >
14181418 < div
14191419 className = "grid text-[9px] leading-none text-muted-foreground/70"
@@ -1477,12 +1477,14 @@ function ActivityGraph({ userId }: { userId: string }) {
14771477 const latestDate = dataPoints . length > 0
14781478 ? parseActivityDate ( dataPoints [ dataPoints . length - 1 ] . date )
14791479 : new Date ( ) ;
1480- const startDate = getMondayWeekStart ( latestDate ) ;
1481- startDate . setUTCDate ( startDate . getUTCDate ( ) - ( ACTIVITY_GRID_ROWS - 1 ) * ACTIVITY_GRID_COLUMNS ) ;
1482-
1480+ // GitHub-style: weeks start on Sunday; grid rows go top → bottom from newest week
1481+ // to oldest; within each row, columns are Sun → Sat.
1482+ const week0Sunday = getSundayWeekStart ( latestDate ) ;
14831483 return Array . from ( { length : ACTIVITY_GRID_CELLS } , ( _ , index ) => {
1484- const date = new Date ( startDate ) ;
1485- date . setUTCDate ( startDate . getUTCDate ( ) + index ) ;
1484+ const row = Math . floor ( index / ACTIVITY_GRID_COLUMNS ) ;
1485+ const col = index % ACTIVITY_GRID_COLUMNS ;
1486+ const date = new Date ( week0Sunday ) ;
1487+ date . setUTCDate ( week0Sunday . getUTCDate ( ) - row * ACTIVITY_GRID_COLUMNS + col ) ;
14861488 const isoDate = formatActivityIsoDate ( date ) ;
14871489 return {
14881490 date : isoDate ,
@@ -1567,7 +1569,7 @@ function UserPage({ user }: { user: ServerUser }) {
15671569
15681570 return (
15691571 < PageLayout >
1570- < div className = "relative flex flex-col gap-6 xl:pr-24 " >
1572+ < div className = "relative flex flex-col gap-6 xl:pr-36 " >
15711573 < RestrictionBanner user = { user } />
15721574 < div className = "absolute right-0 top-0 z-[100] hidden xl:block" >
15731575 < Suspense fallback = { < ActivityLoadingFallback /> } >
0 commit comments