refactor(staffing): decompose 735-line StaffingPanel into focused components
Splits StaffingPanel.tsx into: - StaffingSearchForm: skill tags, dates, hours input, submit button - ScoringExplanation: the 3-column scoring breakdown card - StaffingResultCard: individual suggestion card with details and assign form - StaffingResultsList: list orchestration with loading/empty states - StaffingPanel: thin orchestrator (~100 lines) managing state and tRPC query Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
"use client";
|
||||
|
||||
import { DateInput } from "~/components/ui/DateInput.js";
|
||||
import { SkillTagInput } from "~/components/ui/SkillTagInput.js";
|
||||
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
|
||||
|
||||
export interface SearchCriteria {
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
hoursPerDay: number;
|
||||
}
|
||||
|
||||
interface StaffingSearchFormProps {
|
||||
requiredSkills: string[];
|
||||
onSkillsChange: (skills: string[]) => void;
|
||||
startDate: string;
|
||||
onStartDateChange: (date: string) => void;
|
||||
endDate: string;
|
||||
onEndDateChange: (date: string) => void;
|
||||
hoursPerDay: number;
|
||||
onHoursPerDayChange: (hours: number) => void;
|
||||
onSubmit: () => void;
|
||||
}
|
||||
|
||||
export function StaffingSearchForm({
|
||||
requiredSkills,
|
||||
onSkillsChange,
|
||||
startDate,
|
||||
onStartDateChange,
|
||||
endDate,
|
||||
onEndDateChange,
|
||||
hoursPerDay,
|
||||
onHoursPerDayChange,
|
||||
onSubmit,
|
||||
}: StaffingSearchFormProps) {
|
||||
return (
|
||||
<div className="app-surface-strong p-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">Search Criteria</h2>
|
||||
<p className="mt-1 text-sm text-gray-500">
|
||||
Define the role needs and let the matching engine rank the best candidates.
|
||||
</p>
|
||||
|
||||
<div className="mt-6 space-y-5">
|
||||
<div>
|
||||
<label className="app-label">
|
||||
Required Skills
|
||||
<InfoTooltip content="Skills the candidate must have. The engine scores overlap and proficiency against this list." />
|
||||
</label>
|
||||
<SkillTagInput value={requiredSkills} onChange={onSkillsChange} placeholder="Add skill..." />
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-1">
|
||||
<div>
|
||||
<label className="app-label">Start Date</label>
|
||||
<DateInput value={startDate} onChange={onStartDateChange} className="app-input" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="app-label">End Date</label>
|
||||
<DateInput value={endDate} onChange={onEndDateChange} min={startDate} className="app-input" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="app-label">
|
||||
Hours per Day
|
||||
<InfoTooltip content="Required hours per day for the role. Used to check conflicts and estimate capacity." />
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
value={hoursPerDay}
|
||||
onChange={(e) => onHoursPerDayChange(Number(e.target.value))}
|
||||
min={1}
|
||||
max={24}
|
||||
className="app-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={onSubmit}
|
||||
className="inline-flex w-full items-center justify-center rounded-xl bg-brand-600 px-4 py-3 text-sm font-semibold text-white transition hover:bg-brand-700"
|
||||
>
|
||||
Find Matches
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user