feat: timeline multi-select, demand popover, resource hover card, merged tooltips, dark mode fixes
Major timeline enhancements: - Right-click drag multi-selection with floating action bar (batch delete/assign) - DemandPopover for demand strip details (replaces broken "Loading" modal) - ResourceHoverCard on name hover showing skills, rates, role, chapter - Merged heatmap+vacation tooltips into unified TimelineTooltip component - Fixed overbooking blink animation (date normalization, z-index ordering) - Fixed dark mode sticky column bleed-through in project view - System roles admin page, notification task management, performance review docs Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -25,11 +25,11 @@ const proficiencyLabel: Record<number, string> = {
|
||||
};
|
||||
|
||||
const proficiencyColor: Record<number, string> = {
|
||||
1: "bg-gray-100 text-gray-600",
|
||||
2: "bg-blue-50 text-blue-600",
|
||||
3: "bg-brand-50 text-brand-700",
|
||||
4: "bg-amber-50 text-amber-700",
|
||||
5: "bg-green-50 text-green-700",
|
||||
1: "bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300",
|
||||
2: "bg-blue-50 text-blue-600 dark:bg-blue-900/50 dark:text-blue-300",
|
||||
3: "bg-brand-50 text-brand-700 dark:bg-brand-900/50 dark:text-brand-200",
|
||||
4: "bg-amber-50 text-amber-700 dark:bg-amber-900/50 dark:text-amber-300",
|
||||
5: "bg-green-50 text-green-700 dark:bg-green-900/50 dark:text-green-300",
|
||||
};
|
||||
|
||||
const vacationStatusColor: Record<string, string> = {
|
||||
@@ -211,10 +211,10 @@ export function ResourceDetail({ resourceId }: ResourceDetailProps) {
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-center gap-3 flex-wrap">
|
||||
<h1 className="text-2xl font-bold text-gray-900 truncate">{resource.displayName}</h1>
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100 truncate">{resource.displayName}</h1>
|
||||
<span
|
||||
className={`flex-shrink-0 px-2.5 py-0.5 text-xs font-medium rounded-full ${
|
||||
resource.isActive ? "bg-green-100 text-green-700" : "bg-red-100 text-red-700"
|
||||
resource.isActive ? "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300" : "bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300"
|
||||
}`}
|
||||
>
|
||||
{resource.isActive ? "Active" : "Inactive"}
|
||||
@@ -359,11 +359,11 @@ export function ResourceDetail({ resourceId }: ResourceDetailProps) {
|
||||
|
||||
{/* Profile meta (area role, portfolio, last import) */}
|
||||
{(resourceWithMeta.areaRole || resourceWithMeta.portfolioUrl || resourceWithMeta.skillMatrixUpdatedAt) && (
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-4 flex flex-wrap gap-4 text-sm">
|
||||
<div className="bg-white dark:bg-gray-900 rounded-xl border border-gray-200 dark:border-gray-700 p-4 flex flex-wrap gap-4 text-sm">
|
||||
{resourceWithMeta.areaRole && (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-gray-500 text-xs">Area:</span>
|
||||
<span className="font-medium text-gray-800">{resourceWithMeta.areaRole.name}</span>
|
||||
<span className="text-gray-500 dark:text-gray-400 text-xs">Area:</span>
|
||||
<span className="font-medium text-gray-800 dark:text-gray-200">{resourceWithMeta.areaRole.name}</span>
|
||||
</div>
|
||||
)}
|
||||
{resourceWithMeta.portfolioUrl && (
|
||||
@@ -398,13 +398,13 @@ export function ResourceDetail({ resourceId }: ResourceDetailProps) {
|
||||
|
||||
{/* Main Skills Badges */}
|
||||
{mainSkills.length > 0 && (
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-5">
|
||||
<h2 className="text-sm font-semibold text-gray-800 mb-3 flex items-center">Main Skills<InfoTooltip content="Up to 2 skills flagged as primary strengths. Used for staffing matching priority." /></h2>
|
||||
<div className="bg-white dark:bg-gray-900 rounded-xl border border-gray-200 dark:border-gray-700 p-5">
|
||||
<h2 className="text-sm font-semibold text-gray-800 dark:text-gray-200 mb-3 flex items-center">Main Skills<InfoTooltip content="Up to 2 skills flagged as primary strengths. Used for staffing matching priority." /></h2>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{mainSkills.map((s) => (
|
||||
<span
|
||||
key={s.skill}
|
||||
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded-full bg-amber-50 text-amber-800 border border-amber-200"
|
||||
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded-full bg-amber-50 text-amber-800 border border-amber-200 dark:bg-amber-900/40 dark:text-amber-200 dark:border-amber-700"
|
||||
>
|
||||
<span className="text-amber-500">★</span>
|
||||
{s.skill}
|
||||
@@ -422,8 +422,8 @@ export function ResourceDetail({ resourceId }: ResourceDetailProps) {
|
||||
|
||||
{/* Roles */}
|
||||
{resourceRoles.length > 0 && (
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-5">
|
||||
<h2 className="text-sm font-semibold text-gray-800 mb-3 flex items-center">Roles<InfoTooltip content="Job functions assigned to this resource. The primary role is used in staffing and timeline displays." /></h2>
|
||||
<div className="bg-white dark:bg-gray-900 rounded-xl border border-gray-200 dark:border-gray-700 p-5">
|
||||
<h2 className="text-sm font-semibold text-gray-800 dark:text-gray-200 mb-3 flex items-center">Roles<InfoTooltip content="Job functions assigned to this resource. The primary role is used in staffing and timeline displays." /></h2>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{resourceRoles.map((rr) => (
|
||||
<span
|
||||
@@ -445,13 +445,13 @@ export function ResourceDetail({ resourceId }: ResourceDetailProps) {
|
||||
|
||||
{/* Skills */}
|
||||
{skills.length > 0 && (
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-5">
|
||||
<h2 className="text-sm font-semibold text-gray-800 mb-3 flex items-center">Skills<InfoTooltip content="Full skill inventory with proficiency level (1-5) and years of experience. Imported via skill matrix XLSX." /></h2>
|
||||
<div className="bg-white dark:bg-gray-900 rounded-xl border border-gray-200 dark:border-gray-700 p-5">
|
||||
<h2 className="text-sm font-semibold text-gray-800 dark:text-gray-200 mb-3 flex items-center">Skills<InfoTooltip content="Full skill inventory with proficiency level (1-5) and years of experience. Imported via skill matrix XLSX." /></h2>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{skills.map((s) => (
|
||||
<span
|
||||
key={s.skill}
|
||||
className="inline-flex items-center gap-1.5 px-3 py-1 text-sm rounded-full bg-gray-100 text-gray-700"
|
||||
className="inline-flex items-center gap-1.5 px-3 py-1 text-sm rounded-full bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-200"
|
||||
>
|
||||
{s.skill}
|
||||
{s.proficiency != null && (
|
||||
@@ -464,7 +464,7 @@ export function ResourceDetail({ resourceId }: ResourceDetailProps) {
|
||||
</span>
|
||||
)}
|
||||
{s.yearsExperience != null && (
|
||||
<span className="text-xs text-gray-400">{s.yearsExperience}y</span>
|
||||
<span className="text-xs text-gray-400 dark:text-gray-500">{s.yearsExperience}y</span>
|
||||
)}
|
||||
</span>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user