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,74 @@
|
||||
"use client";
|
||||
|
||||
import { StaffingResultCard, type SuggestionLike } from "./StaffingResultCard.js";
|
||||
import type { SearchCriteria } from "./StaffingSearchForm.js";
|
||||
|
||||
interface StaffingResultsListProps {
|
||||
suggestions: SuggestionLike[] | undefined;
|
||||
isLoading: boolean;
|
||||
submitted: boolean;
|
||||
searchCriteria: SearchCriteria;
|
||||
assignedIds: Set<string>;
|
||||
onAssigned: (resourceId: string, resourceName: string) => void;
|
||||
onError: (message: string) => void;
|
||||
}
|
||||
|
||||
export function StaffingResultsList({
|
||||
suggestions,
|
||||
isLoading,
|
||||
submitted,
|
||||
searchCriteria,
|
||||
assignedIds,
|
||||
onAssigned,
|
||||
onError,
|
||||
}: StaffingResultsListProps) {
|
||||
const visible = suggestions?.filter((s) => !assignedIds.has(s.resourceId));
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="app-surface-strong py-16 text-center text-sm text-gray-500">
|
||||
Finding best matches...
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (visible && visible.length === 0) {
|
||||
return (
|
||||
<div className="app-surface-strong py-16 text-center text-sm text-gray-500">
|
||||
{assignedIds.size > 0
|
||||
? "All suggestions have been assigned."
|
||||
: "No resources found matching your criteria."}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!submitted) {
|
||||
return (
|
||||
<div className="app-surface-strong border-dashed py-20 text-center">
|
||||
<div className="mx-auto max-w-md">
|
||||
<div className="text-sm font-medium text-gray-900 dark:text-gray-100">No suggestions yet</div>
|
||||
<p className="mt-2 text-sm text-gray-500">
|
||||
Add the required skills and date range, then run the search to see ranked staffing matches.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!visible) return null;
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{visible.map((suggestion, idx) => (
|
||||
<StaffingResultCard
|
||||
key={suggestion.resourceId}
|
||||
suggestion={suggestion}
|
||||
rank={idx + 1}
|
||||
searchCriteria={searchCriteria}
|
||||
onAssigned={onAssigned}
|
||||
onError={onError}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user