Skip to content

feat(standup): add occupancy slide - #30

Merged
tnagorra merged 1 commit into
developfrom
feature/standup-occupancy
Oct 5, 2026
Merged

tnagorra merged 1 commit into
developfrom
feature/standup-occupancy

Conversation

@thenav56

@thenav56 thenav56 commented Oct 4, 2026

Copy link
Copy Markdown
Member

Summary

Adds an "Is your Timur up to date?" slide to the daily standup, between the last project slide and the quote. It shows each person's logged hours per working day, so people who haven't filled in their entries or leave are easy to spot.

Requires the backend dailyStandup.occupancy field:

What the slide shows

  • Range: the current month up to yesterday. During the first 7 days of a month, the previous month is included too.
  • Cells: logged hours as a % of 7h per working day. Weekends and holidays are skipped.
    • Red ?: no entries and no leave.
    • Leave icon: full-day leave.
    • Corner mark: half-day leave.
  • Missing column, next to each name:
    • Red: has gaps older than the last 2 working days.
    • Amber: only the last 2 working days are missing.
    • ✓: nothing missing.
  • Order: red first, then amber, then everyone else. Within each group, more missing days first, then the oldest gap first.
  • Headers: month, ISO week (W36) and day rows, with gaps between weeks and a line between months.
  • Long names are shortened with an ellipsis; hover a name or cell for details.

Other changes

  • Adds --color-warning and --color-warning-text to themes.css (light and dark).

Screenshots

image

Testing

  • Checked against a prod data dump: values match the existing Superset occupancy query cell for cell (437/437).
  • Checked light and dark themes, and a phone-width viewport (the table scrolls sideways with names pinned).
  • pnpm typecheck, pnpm lint, pnpm lint:unused pass.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@thenav56
thenav56 force-pushed the feature/standup-occupancy branch from 386492f to b2fca58 Compare October 4, 2026 03:07
Comment thread src/themes.css
--color-info: #0277bd;
--color-info-text: #01579b;

--color-warning: #f9a825;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to check if the colorschemes need these.

displayName
displayPicture
}
days {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For normalized cache, we should always fetch the unique key.
By default, id is the unique key.
If the unique key is different, then it needs to be defined so that the normalized cache works as expected.

Comment on lines +65 to +68
function parseDate(value: string) {
const [year, month, day] = value.split('-').map(Number);
return new Date(year ?? 0, (month ?? 1) - 1, day ?? 1);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check if we don't have these functions in @togglecorp/fujs
Only implement the functions if they are not in fujs.
Also, suggest if this needs to go into fujs if it's generic enough.

getKey: (date: Date, value: string) => string,
getLabel: (date: Date) => string,
) {
return dates.reduce<DateGroup[]>(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When using map/reduce, do not mutate the entities.
If you want to mutate, use forEach or for loop

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this example, let's use listToGroupList and mapToList from @togglecorp/fujs library for better readability

return isNotDefined(day.occupancy);
}

function getLeaveLabel(leave: JournalLeaveTypeEnum | null | undefined) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is a ENUM, use enum_display for the label for the ENUM

styles.cell,
boundaryClassName,
day.leave && styles.halfLeave,
day.occupancy < 0.5 ? styles.low : styles.high,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's have 3 scale for the color depending on the percent:

  • 66 to 100 (green)
  • 33 to 66 (yellow)
  • 0 to 33 (red)

Comment on lines +299 to +304
let missingSeverity = 2;
if (hasOlderMissing) {
missingSeverity = 0;
} else if (missingDays.length > 0) {
missingSeverity = 1;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rename this to severity.
0 is low severity
2 is high severity

Then for severity we can sort descending

}
return {
...item,
displayName: item.user.displayName ?? `User#${item.user.id}`,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The displayName should always be provided by the backend so that front-end does not need to add a fallback.

className={styles.occupancySection}
variant="general"
>
<div className={styles.content}>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check if we can use the existing component for the slide page

@tnagorra
tnagorra merged commit ca006fa into develop Oct 5, 2026
6 checks passed
@tnagorra
tnagorra deleted the feature/standup-occupancy branch October 5, 2026 13:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants