feat: Implement revenue/profit recap and fixes

- Added a hover-over recap for total revenue and profit, displaying contributing jobs in a popup.
- Fixed the issue where the lower parts of letters in job names were cut off.
- Implemented job ID copy-to-clipboard functionality on click.
This commit is contained in:
gpt-engineer-app[bot]
2025-07-07 17:40:12 +00:00
committed by PhatPhuckDave
parent 2e576b6d28
commit cb32ccaba9
3 changed files with 172 additions and 37 deletions

View File

@@ -1,3 +1,4 @@
import { useState } from 'react';
import { CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
@@ -32,6 +33,7 @@ const JobCardHeader: React.FC<JobCardHeaderProps> = ({
const [producedValue, setProducedValue] = useState(job.produced?.toString() || '0');
const [copyingBom, setCopyingBom] = useState(false);
const [copyingName, setCopyingName] = useState(false);
const [copyingId, setCopyingId] = useState(false);
const { toast } = useToast();
const { updateJob } = useJobs();
@@ -196,6 +198,27 @@ const JobCardHeader: React.FC<JobCardHeaderProps> = ({
}
};
const handleJobIdClick = async (e: React.MouseEvent) => {
e.stopPropagation();
try {
await navigator.clipboard.writeText(job.id);
setCopyingId(true);
toast({
title: "Copied!",
description: "Job ID copied to clipboard",
duration: 2000,
});
setTimeout(() => setCopyingId(false), 1000);
} catch (err) {
toast({
title: "Error",
description: "Failed to copy to clipboard",
variant: "destructive",
duration: 2000,
});
}
};
const handleProducedClick = (e: React.MouseEvent) => {
if (job.status !== 'Closed') {
setIsEditingProduced(true);
@@ -228,47 +251,61 @@ const JobCardHeader: React.FC<JobCardHeaderProps> = ({
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-2">
<CardTitle
className="text-blue-400 truncate cursor-pointer hover:text-blue-300 transition-colors flex items-center gap-1"
className="text-blue-400 truncate cursor-pointer hover:text-blue-300 transition-colors flex items-center gap-1 leading-normal"
onClick={handleJobNameClick}
title="Click to copy job name"
data-no-navigate
style={{ lineHeight: '1.4' }}
>
{job.outputItem}
{copyingName && <Copy className="w-4 h-4 text-green-400" />}
</CardTitle>
</div>
<p className="text-gray-400 text-sm">
Runs: {job.outputQuantity.toLocaleString()}
<span className="ml-4">
Produced: {
isEditingProduced && job.status !== 'Closed' ? (
<Input
type="number"
value={producedValue}
onChange={(e) => setProducedValue(e.target.value)}
onBlur={handleProducedUpdate}
onKeyDown={handleProducedKeyPress}
className="w-24 h-5 px-2 py-0 inline-block bg-gray-800 border-gray-600 text-white text-xs leading-5"
min="0"
autoFocus
data-no-navigate
/>
) : (
<span
onClick={handleProducedClick}
className={`inline-block w-20 h-5 leading-5 text-left ${job.status !== 'Closed' ? "cursor-pointer hover:text-blue-400" : ""}`}
title={job.status !== 'Closed' ? "Click to edit" : undefined}
data-no-navigate
>
{(job.produced || 0).toLocaleString()}
</span>
)
}
</span>
<span className="ml-4">
Sold: <span className="text-green-400">{itemsSold.toLocaleString()}</span>
</span>
</p>
<div className="text-gray-400 text-sm leading-relaxed" style={{ lineHeight: '1.4' }}>
<div className="mb-1">
Runs: {job.outputQuantity.toLocaleString()}
<span className="ml-4">
Produced: {
isEditingProduced && job.status !== 'Closed' ? (
<Input
type="number"
value={producedValue}
onChange={(e) => setProducedValue(e.target.value)}
onBlur={handleProducedUpdate}
onKeyDown={handleProducedKeyPress}
className="w-24 h-5 px-2 py-0 inline-block bg-gray-800 border-gray-600 text-white text-xs leading-5"
min="0"
autoFocus
data-no-navigate
/>
) : (
<span
onClick={handleProducedClick}
className={`inline-block w-20 h-5 leading-5 text-left ${job.status !== 'Closed' ? "cursor-pointer hover:text-blue-400" : ""}`}
title={job.status !== 'Closed' ? "Click to edit" : undefined}
data-no-navigate
>
{(job.produced || 0).toLocaleString()}
</span>
)
}
</span>
<span className="ml-4">
Sold: <span className="text-green-400">{itemsSold.toLocaleString()}</span>
</span>
</div>
<div>
ID: <span
className="cursor-pointer hover:text-blue-400 transition-colors inline-flex items-center gap-1"
onClick={handleJobIdClick}
title="Click to copy job ID"
data-no-navigate
>
{job.id}
{copyingId && <Copy className="w-3 h-3 text-green-400" />}
</span>
</div>
</div>
</div>
<div className="flex flex-col gap-2 flex-shrink-0 items-end">
<div className="flex items-center gap-2">