update position page design
This commit is contained in:
@@ -5,7 +5,10 @@ import { cn } from '@/lib/utils';
|
||||
import { Surface } from '@/components/ds/Surface';
|
||||
import { IconButton } from '@/components/ds/IconButton';
|
||||
import { Alert } from '@/components/ds/Alert';
|
||||
import { useCreateJobPosting, usePreferences, useRoleCategories } from '@/lib/krowHooks';
|
||||
import {
|
||||
useAssignments, useAssignWorkers, useCreateJobPosting, useMarkInterviewReady,
|
||||
usePreferences, useRoleCategories,
|
||||
} from '@/lib/krowHooks';
|
||||
import { ROLE_CATEGORIES } from '@/lib/roleCategories';
|
||||
import { AddSkillDialog } from '@/components/skills/AddSkillDialog';
|
||||
import { runAction } from '@/lib/skills/actions';
|
||||
@@ -114,12 +117,67 @@ export default function KrowAssistant({
|
||||
return createJob.mutateAsync(result.data);
|
||||
}, [createJob]);
|
||||
|
||||
/**
|
||||
* The workforce picture the panel reasons over.
|
||||
*
|
||||
* The same collections the Positions page reads, so a recommendation and the
|
||||
* card behind it are looking at one moment. Assembled here rather than in the
|
||||
* routing layer because this is where the app's data already is; routing stays
|
||||
* pure and testable.
|
||||
*/
|
||||
const { data: assignments = [] } = useAssignments();
|
||||
const workforce = React.useMemo(() => ({
|
||||
positions: facts.postings || [],
|
||||
currentPositionId: null,
|
||||
context: {
|
||||
profiles: facts.profiles || [],
|
||||
applications: facts.applications || [],
|
||||
assignments,
|
||||
courses: facts.forge?.library || [],
|
||||
staff: facts.staff || [],
|
||||
},
|
||||
}), [facts.postings, facts.profiles, facts.applications, facts.forge, facts.staff, assignments]);
|
||||
|
||||
/**
|
||||
* The workforce write, through the mutation the app already has.
|
||||
*
|
||||
* `useAssignWorkers` creates the assignment, moves the application and logs
|
||||
* the activity — one path, reused rather than reimplemented. It only ever runs
|
||||
* on a turn the admin explicitly confirmed.
|
||||
*/
|
||||
const assignWorkers = useAssignWorkers();
|
||||
const onAssignWorkers = React.useCallback(async (plan) => {
|
||||
const created = await assignWorkers.mutateAsync({
|
||||
position: plan.position,
|
||||
workers: plan.workers,
|
||||
applications: facts.applications || [],
|
||||
});
|
||||
return created?.length ? created : null;
|
||||
}, [assignWorkers, facts.applications]);
|
||||
|
||||
/**
|
||||
* Move an assigned candidate to interview stage.
|
||||
*
|
||||
* The status transition the Position and Candidates pages already perform —
|
||||
* not a new interview record. `AIInterviewModal` writes the AIInterview when
|
||||
* the interview is actually conducted, and creating an empty one here would be
|
||||
* a second interview store claiming something that has not happened.
|
||||
*/
|
||||
const markInterviewReady = useMarkInterviewReady();
|
||||
const onScheduleInterview = React.useCallback(
|
||||
(plan) => markInterviewReady.mutateAsync({ application: plan.application, position: plan.position }),
|
||||
[markInterviewReady]
|
||||
);
|
||||
|
||||
const { messages, pending, error, busy, send, stop, reset } = useConversation({
|
||||
contextId: context.id,
|
||||
facts,
|
||||
onNavigate: goToPage,
|
||||
onAction: performAction,
|
||||
onCreatePosition: createPosition,
|
||||
onAssignWorkers,
|
||||
onScheduleInterview,
|
||||
workforce,
|
||||
disabledSkills,
|
||||
customSkills,
|
||||
roles,
|
||||
@@ -127,6 +185,13 @@ export default function KrowAssistant({
|
||||
courses: facts.forge?.library || [],
|
||||
});
|
||||
|
||||
/* A block inside an answer asking the next question, in place. Same entry
|
||||
point as the composer and the chips, so an in-panel action is an ordinary
|
||||
turn rather than a second way for the panel to change. */
|
||||
const askOwliver = React.useCallback((question) => {
|
||||
if (!busy) send({ question });
|
||||
}, [busy, send]);
|
||||
|
||||
const [input, setInput] = React.useState('');
|
||||
const scrollRef = React.useRef(null);
|
||||
const isEmpty = messages.length === 0 && !pending;
|
||||
@@ -146,8 +211,8 @@ export default function KrowAssistant({
|
||||
const skillPrompts = skills
|
||||
.filter((s) => s.prompt)
|
||||
.map((s) => ({ label: s.prompt, prompt: s.prompt }));
|
||||
return [...skillPrompts, ...buildPrompts(context.id, facts)];
|
||||
}, [followUp, skills, context.id, facts]);
|
||||
return [...skillPrompts, ...buildPrompts(context.id, facts, workforce)];
|
||||
}, [followUp, skills, context.id, facts, workforce]);
|
||||
|
||||
/* Follow the newest content. Direct `scrollTop` rather than smooth scrolling:
|
||||
at streaming frequency a smooth scroll never catches up and the thread
|
||||
@@ -281,6 +346,7 @@ export default function KrowAssistant({
|
||||
text={message.text}
|
||||
blocks={message.blocks}
|
||||
stopped={message.stopped}
|
||||
onPrompt={askOwliver}
|
||||
/>
|
||||
</React.Fragment>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user