update position page design

This commit is contained in:
2026-08-13 21:41:14 +05:30
parent 6340962907
commit 1c653264b5
124 changed files with 5746 additions and 596 deletions

View File

@@ -1,4 +1,5 @@
import { plural, verb } from './insights';
import { prioritise, workforceStatusFor } from '@/lib/workforce';
import { PERMISSIONS } from '@/pages/admin/permissions';
/**
@@ -390,18 +391,71 @@ const PROMPTS = {
{ label: 'Identify operational risks', prompt: 'Identify operational risks and what I should fix first', capability: 'recommendations' },
],
'admin.positions': (f) => [
f.starvedPositions.length
? { label: `${f.starvedPositions.length} roles need attention`, prompt: 'What needs attention across my positions?', capability: 'positions-attention' }
: { label: 'What needs attention?', prompt: 'What needs attention across my positions?', capability: 'positions-attention' },
{ label: 'Which position has the strongest pipeline?', prompt: 'Which position has the strongest pipeline?', capability: 'position-strength' },
f.unscreened.length
? { label: `${f.unscreened.length} waiting for review`, prompt: 'Show candidates waiting for review', capability: 'candidates-waiting' }
: { label: 'Show candidates waiting for review', prompt: 'Show candidates waiting for review', capability: 'candidates-waiting' },
{ label: 'Which position should I hire for first?', prompt: 'Which position should I hire for first?', capability: 'hiring-priority' },
{ label: 'Summarize hiring activity', prompt: 'Summarize hiring activity across my positions', capability: 'hiring-operations' },
{ label: 'Identify hiring bottlenecks', prompt: 'Identify hiring bottlenecks', capability: 'pipeline-health' },
],
/**
* Positions suggestions are the current workforce situation, phrased as
* questions.
*
* Each one is offered only when the data behind it exists — a prompt about
* strong matches on a board with none is an invitation to a dead end. None of
* them names a person: the question is asked generically and the candidates
* are resolved live, so a suggestion cannot go stale between being rendered
* and being clicked.
*
* Workforce prompts carry no `capability`, which is what sends them through
* intent routing to the engine rather than to a page analytics responder.
*/
'admin.positions': (f, workforce) => {
const out = [];
if (workforce?.positions?.length) {
const { positions, context } = workforce;
const ranked = prioritise(positions, context);
const board = positions
.filter((p) => p.status === 'active')
.map((p) => ({ position: p, status: workforceStatusFor(p, context) }));
const withStrong = board.filter((r) => r.status.strong.length);
const newToday = board.reduce((n, r) => n + r.status.newToday.length, 0);
const readyCount = board.reduce(
(n, r) => n + r.status.applicants.filter(
(a) => ['ai_screened', 'shortlisted'].includes(a.application.status)
).length,
0
);
if (withStrong.length) {
const best = withStrong.sort((a, b) => b.status.strong.length - a.status.strong.length)[0];
out.push({
label: `${best.status.strong.length} strong for ${best.position.title}`,
prompt: `Who matches ${best.position.title}?`,
});
}
if (ranked.length) {
out.push({ label: 'Which position needs people first?', prompt: 'Which position needs people first?' });
}
if (newToday) {
out.push({ label: `${newToday} applied today`, prompt: 'Who applied today?' });
}
if (readyCount) {
out.push({ label: `${readyCount} ready for interview`, prompt: 'Who is ready for interview?' });
}
}
/* The page's own analytics questions keep their place behind the workforce
ones — they are still the right answer to "how is hiring going". */
out.push(
f.starvedPositions.length
? { label: `${f.starvedPositions.length} roles need attention`, prompt: 'What needs attention across my positions?', capability: 'positions-attention' }
: { label: 'What needs attention?', prompt: 'What needs attention across my positions?', capability: 'positions-attention' },
{ label: 'Which position has the strongest pipeline?', prompt: 'Which position has the strongest pipeline?', capability: 'position-strength' },
f.unscreened.length
? { label: `${f.unscreened.length} waiting for review`, prompt: 'Show candidates waiting for review', capability: 'candidates-waiting' }
: { label: 'Show candidates waiting for review', prompt: 'Show candidates waiting for review', capability: 'candidates-waiting' },
{ label: 'Summarize hiring activity', prompt: 'Summarize hiring activity across my positions', capability: 'hiring-operations' }
);
return out;
},
'admin.candidatesList': (f) => [
f.stalled.length
@@ -548,6 +602,6 @@ export function buildIntro(contextId, facts, userName) {
}
/** Suggested prompts for the current page and data. */
export function buildPrompts(contextId, facts) {
return PROMPTS[contextId]?.(facts) ?? [];
export function buildPrompts(contextId, facts, workforce = null) {
return PROMPTS[contextId]?.(facts, workforce) ?? [];
}