Skip to content

Commit 9d16950

Browse files
authored
Fix queue preferences modal title, slot time formatting, timepicker h… (#541)
* Fix queue preferences modal title, slot time formatting, timepicker hours - Fix modal title 'Interview Queue Preferences' (26 chars) → 'Queue Preferences' to stay under Slack's 25-char limit - Format slot times as 'Mon, Apr 6, 4 PM–8 PM' instead of raw ISO strings - Restrict pairing session timepickers to 8 AM–8 PM * Add tests for formatSlot
1 parent 4211431 commit 9d16950

7 files changed

Lines changed: 53 additions & 8 deletions

File tree

src/bot/acceptPairingSlot.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { pairingSessionCloser } from '@/services/PairingSessionCloser';
99
import { reviewLockManager } from '@utils/reviewLockManager';
1010
import { lockedExecute } from '@utils/lockedExecute';
1111
import { reportErrorAndContinue } from '@utils/reportError';
12-
import { bold, compose, textBlock, ul } from '@utils/text';
12+
import { bold, compose, formatSlot, textBlock, ul } from '@utils/text';
1313
import { chatService } from '@/services/ChatService';
1414

1515
export const acceptPairingSlot = {
@@ -69,7 +69,7 @@ export const acceptPairingSlot = {
6969
);
7070

7171
const selectedSlots = interview.slots.filter(s => selectedSlotIds.includes(s.id));
72-
const slotLines = selectedSlots.map(s => `${s.date}, ${s.startTime}${s.endTime}`);
72+
const slotLines = selectedSlots.map(s => formatSlot(s.date, s.startTime, s.endTime));
7373
await chatService.updateDirectMessage(client, userId, messageTimestamp, [
7474
textBlock(
7575
compose(

src/bot/joinQueue.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export const joinQueue = {
2929

3030
dialog(languages: string[]): View {
3131
return {
32-
title: { text: 'Interview Queue Preferences', type: 'plain_text' },
32+
title: { text: 'Queue Preferences', type: 'plain_text' },
3333
type: 'modal',
3434
callback_id: Interaction.SUBMIT_JOIN_QUEUE,
3535
blocks: [

src/bot/requestPairingSession.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,8 +339,10 @@ function buildSlotBlocks(slotNumber: number, state?: SlotState): (Block | KnownB
339339
element: {
340340
type: 'timepicker',
341341
action_id: startId,
342+
min_time: '08:00',
343+
max_time: '20:00',
342344
...(state?.startTime ? { initial_time: state.startTime } : {}),
343-
},
345+
} as any,
344346
},
345347
{
346348
type: 'input',
@@ -349,8 +351,10 @@ function buildSlotBlocks(slotNumber: number, state?: SlotState): (Block | KnownB
349351
element: {
350352
type: 'timepicker',
351353
action_id: endId,
354+
min_time: '08:00',
355+
max_time: '20:00',
352356
...(state?.endTime ? { initial_time: state.endTime } : {}),
353-
},
357+
} as any,
354358
},
355359
];
356360
}

src/services/PairingSessionCloser.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { pairingSessionsRepo } from '@repos/pairingSessionsRepo';
44
import { userRepo } from '@repos/userRepo';
55
import { chatService } from '@/services/ChatService';
66
import { App } from '@slack/bolt';
7-
import { mention, ul } from '@utils/text';
7+
import { formatSlot, mention, ul } from '@utils/text';
88
import { reviewLockManager } from '@utils/reviewLockManager';
99
import log from '@utils/log';
1010

@@ -47,7 +47,7 @@ export const pairingSessionCloser = {
4747
.map(t => [t.userId, t] as [string, typeof t]),
4848
).values(),
4949
);
50-
const slotLines = confirmedSlots.map(s => `${s.date}, ${s.startTime}${s.endTime}`);
50+
const slotLines = confirmedSlots.map(s => formatSlot(s.date, s.startTime, s.endTime));
5151
await chatService.replyToReviewThread(
5252
app.client,
5353
threadId,

src/services/__tests__/PairingSessionCloser.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ describe('PairingSessionCloser', () => {
174174
expect(chatService.replyToReviewThread).toHaveBeenCalledWith(
175175
app.client,
176176
'thread-1',
177-
expect.stringContaining('2026-03-31'),
177+
expect.stringContaining('Mar 31'),
178178
);
179179
expect(userRepo.markNowAsLastReviewedDate).toHaveBeenCalledWith('u1');
180180
expect(userRepo.markNowAsLastReviewedDate).toHaveBeenCalledWith('u2');

src/utils/__tests__/text.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { formatSlot } from '../text';
2+
3+
describe('formatSlot', () => {
4+
it('formats a slot with whole-hour times', () => {
5+
expect(formatSlot('2026-04-06', '16:00', '20:00')).toBe('Mon, Apr 6, 4 PM–8 PM');
6+
});
7+
8+
it('formats a slot with minute-precision times', () => {
9+
expect(formatSlot('2026-04-06', '09:30', '11:00')).toBe('Mon, Apr 6, 9:30 AM–11 AM');
10+
});
11+
12+
it('formats noon and midnight boundaries correctly', () => {
13+
expect(formatSlot('2026-04-07', '12:00', '13:00')).toBe('Tue, Apr 7, 12 PM–1 PM');
14+
});
15+
16+
it('formats 8 AM start time correctly', () => {
17+
expect(formatSlot('2026-04-10', '08:00', '20:00')).toBe('Fri, Apr 10, 8 AM–8 PM');
18+
});
19+
20+
it('includes the correct day of week', () => {
21+
expect(formatSlot('2026-03-31', '13:00', '15:00')).toBe('Tue, Mar 31, 1 PM–3 PM');
22+
});
23+
});

src/utils/text.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,24 @@ export function textBlock(text: string): KnownBlock {
4949
};
5050
}
5151

52+
export function formatSlot(date: string, startTime: string, endTime: string): string {
53+
const [year, month, day] = date.split('-').map(Number);
54+
const dateObj = new Date(year, month - 1, day);
55+
const dayOfWeek = dateObj.toLocaleDateString('en-US', { weekday: 'short' });
56+
const monthStr = dateObj.toLocaleDateString('en-US', { month: 'short' });
57+
return `${dayOfWeek}, ${monthStr} ${day}, ${formatTime(startTime)}${formatTime(endTime)}`;
58+
}
59+
60+
function formatTime(hhmm: string): string {
61+
const [hourStr, minuteStr] = hhmm.split(':');
62+
const hour = Number(hourStr);
63+
const minute = Number(minuteStr);
64+
const period = hour >= 12 ? 'PM' : 'AM';
65+
const hour12 = hour % 12 || 12;
66+
const minuteDisplay = minute === 0 ? '' : `:${minuteStr}`;
67+
return `${hour12}${minuteDisplay} ${period}`;
68+
}
69+
5270
export function errorStack(err: Error): string {
5371
return err.stack ?? '[no stack trace]';
5472
}

0 commit comments

Comments
 (0)