Add job card glow effect

Highlight job cards based on completion criteria for "Acquisition" and "Selling" jobs.
This commit is contained in:
gpt-engineer-app[bot]
2025-07-09 22:32:01 +00:00
committed by PhatPhuckDave
parent 8b2c85df33
commit d05d0180b8
2 changed files with 51 additions and 1 deletions

View File

@@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom';
import { Card, CardContent, CardHeader } from '@/components/ui/card';
import { IndJob } from '@/lib/types';
import { getStatusBackgroundColor } from '@/utils/jobStatusUtils';
import { jobNeedsAttention, getAttentionGlowClasses } from '@/utils/jobAttentionUtils';
import JobCardHeader from './JobCardHeader';
import JobCardDetails from './JobCardDetails';
import JobCardMetrics from './JobCardMetrics';
@@ -25,6 +26,7 @@ const JobCard: React.FC<JobCardProps> = ({
isTracked = false
}) => {
const navigate = useNavigate();
const needsAttention = jobNeedsAttention(job);
const handleCardClick = (e: React.MouseEvent) => {
const target = e.target as HTMLElement;
@@ -39,7 +41,7 @@ const JobCard: React.FC<JobCardProps> = ({
return (
<Card
className={`bg-gray-900 border-gray-700 text-white h-full flex flex-col cursor-pointer hover:bg-gray-800/50 transition-colors ${job.status === 'Tracked' ? 'border-l-4 border-l-cyan-600' : ''} ${getStatusBackgroundColor(job.status)}`}
className={`bg-gray-900 border-gray-700 text-white h-full flex flex-col cursor-pointer hover:bg-gray-800/50 transition-colors ${job.status === 'Tracked' ? 'border-l-4 border-l-cyan-600' : ''} ${getStatusBackgroundColor(job.status)} ${needsAttention ? getAttentionGlowClasses() : ''}`}
onClick={handleCardClick}
>
<CardHeader className="flex-shrink-0">

View File

@@ -0,0 +1,48 @@
import { IndJob } from '@/lib/types';
export function jobNeedsAttention(job: IndJob): boolean {
// Acquisition jobs need attention when all materials are satisfied
if (job.status === 'Acquisition') {
if (!job.billOfMaterials || job.billOfMaterials.length === 0) {
return false;
}
// Create a map of required materials from bill of materials
const requiredMaterials = new Map<string, number>();
job.billOfMaterials.forEach(item => {
requiredMaterials.set(item.name, item.quantity);
});
// Create a map of owned materials from expenditures
const ownedMaterials = new Map<string, number>();
job.expenditures?.forEach(transaction => {
const currentOwned = ownedMaterials.get(transaction.itemName) || 0;
ownedMaterials.set(transaction.itemName, currentOwned + transaction.quantity);
});
// Check if all materials are satisfied
let allMaterialsSatisfied = true;
requiredMaterials.forEach((required, materialName) => {
const owned = ownedMaterials.get(materialName) || 0;
if (owned < required) {
allMaterialsSatisfied = false;
}
});
return allMaterialsSatisfied;
}
// Selling jobs need attention when sold count reaches produced count
if (job.status === 'Selling') {
const produced = job.produced || 0;
const sold = job.income?.reduce((sum, tx) => sum + tx.quantity, 0) || 0;
return sold >= produced && produced > 0;
}
return false;
}
export function getAttentionGlowClasses(): string {
return 'ring-2 ring-yellow-400/50 shadow-lg shadow-yellow-400/20 animate-pulse';
}