Skip to content

Commit ca2c8cb

Browse files
henningmuclaude
andauthored
fix: Match HiBob API field names for timeoff display (#6)
## Summary - Add `employeeDisplayName` and `policyTypeDisplayName` to field extractors — the actual field names returned by the HiBob API — fixing "Unknown" names and missing type labels in `outtoday`/`whosout` output - Add a count header ("N people out") and remove redundant email column from text output - Include the actual API field names in `TIMEOFF_ESSENTIAL_FIELDS` for JSON/NDJSON output ## Test plan - [x] `bob outtoday` shows real names and type labels instead of "Unknown" - [x] `bob whosout --from ... --to ...` same - [x] `bob outtoday --json` includes `employeeDisplayName`, `policyTypeDisplayName`, `employeeEmail` - [x] `bob outtoday --ndjson` same - [x] Pre-commit hooks pass (biome lint + type-check) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Henning Muszynski <henningmu@users.noreply.github.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent e7dceb7 commit ca2c8cb

File tree

2 files changed

+25
-16
lines changed

2 files changed

+25
-16
lines changed

src/commands/timeoff.ts

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ function getNestedString(value: unknown, path: string[]): string | undefined {
2020

2121
function getDisplayName(entry: Record<string, unknown>): string {
2222
const nameFields = [
23+
entry.employeeDisplayName,
2324
entry.displayName,
2425
entry.name,
2526
entry.employeeName,
@@ -32,20 +33,9 @@ function getDisplayName(entry: Record<string, unknown>): string {
3233
return 'Unknown'
3334
}
3435

35-
function getEmail(entry: Record<string, unknown>): string {
36-
const emailFields = [
37-
entry.email,
38-
entry.employeeEmail,
39-
getNestedString(entry, ['employee', 'email']),
40-
]
41-
for (const candidate of emailFields) {
42-
if (typeof candidate === 'string' && candidate.length > 0) return candidate
43-
}
44-
return ''
45-
}
46-
4736
function getType(entry: Record<string, unknown>): string {
4837
const typeFields = [
38+
entry.policyTypeDisplayName,
4939
entry.type,
5040
entry.policyType,
5141
entry.timeOffType,
@@ -114,26 +104,38 @@ function extractTimeoffList(data: unknown): Record<string, unknown>[] {
114104

115105
function renderTimeoffRow(entry: Record<string, unknown>): string {
116106
const name = getDisplayName(entry)
117-
const email = getEmail(entry)
118107
const type = getType(entry)
119108
const dates = formatDateRange(entry)
120109

121110
const details: string[] = []
122-
if (email) details.push(chalk.dim(email))
123111
if (type) details.push(chalk.cyan(type))
124112
if (dates) details.push(chalk.yellow(dates))
125113

126114
return [chalk.bold(name), details.join(' ')].filter(Boolean).join(' ')
127115
}
128116

117+
function renderTimeoffList(entries: Record<string, unknown>[]): string {
118+
const lines: string[] = []
119+
lines.push(chalk.dim(`${entries.length} people out`))
120+
lines.push('')
121+
for (const entry of entries) {
122+
lines.push(renderTimeoffRow(entry))
123+
}
124+
return lines.join('\n')
125+
}
126+
129127
async function listWhosOut(options: DateRangeOptions): Promise<void> {
130128
const params = new URLSearchParams()
131129
if (options.from) params.set('from', options.from)
132130
if (options.to) params.set('to', options.to)
133131
const path = params.toString() ? `/timeoff/whosout?${params.toString()}` : '/timeoff/whosout'
134132
const response = await apiGet(path)
135133
const entries = extractTimeoffList(response)
136-
outputList(entries, options, 'timeoff', renderTimeoffRow)
134+
if (options.json || options.ndjson) {
135+
outputList(entries, options, 'timeoff')
136+
} else {
137+
console.log(renderTimeoffList(entries))
138+
}
137139
}
138140

139141
async function listOutToday(options: DateRangeOptions): Promise<void> {
@@ -142,7 +144,11 @@ async function listOutToday(options: DateRangeOptions): Promise<void> {
142144
const path = params.toString() ? `/timeoff/outtoday?${params.toString()}` : '/timeoff/outtoday'
143145
const response = await apiGet(path)
144146
const entries = extractTimeoffList(response)
145-
outputList(entries, options, 'timeoff', renderTimeoffRow)
147+
if (options.json || options.ndjson) {
148+
outputList(entries, options, 'timeoff')
149+
} else {
150+
console.log(renderTimeoffList(entries))
151+
}
146152
}
147153

148154
export function registerTimeoffCommand(program: Command): void {

src/lib/output.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ const PERSON_ESSENTIAL_FIELDS = [
1414
const TIMEOFF_ESSENTIAL_FIELDS = [
1515
'id',
1616
'employeeId',
17+
'employeeDisplayName',
1718
'displayName',
19+
'employeeEmail',
1820
'email',
21+
'policyTypeDisplayName',
1922
'type',
2023
'policyType',
2124
'status',

0 commit comments

Comments
 (0)