Fix Job Card layout and tax display
Refactor Job Card layout to a grid, and adjust tax display.
This commit is contained in:
@@ -69,42 +69,70 @@ const JobCardDetails: React.FC<JobCardDetailsProps> = ({ job }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const formatDateForInput = (dateString: string | null | undefined) => {
|
||||||
|
if (!dateString) return '';
|
||||||
|
return new Date(dateString).toISOString().slice(0, 16);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBlur = (fieldName: string) => {
|
||||||
|
const value = tempValues[fieldName];
|
||||||
|
if (value !== (job[fieldName as keyof IndJob] || '')) {
|
||||||
|
handleFieldUpdate(fieldName, value);
|
||||||
|
} else {
|
||||||
|
setEditingField(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClick = (fieldName: string, value: string | null, e: React.MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (value) {
|
||||||
|
setEditingField(fieldName);
|
||||||
|
setTempValues({ ...tempValues, [fieldName]: formatDateForInput(value) });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const DateField = ({ label, value, fieldName, icon }: { label: string; value: string | null; fieldName: string; icon: React.ReactNode }) => (
|
const DateField = ({ label, value, fieldName, icon }: { label: string; value: string | null; fieldName: string; icon: React.ReactNode }) => (
|
||||||
<div className="flex items-center gap-2 text-sm text-gray-400">
|
<>
|
||||||
{icon}
|
<div className="flex items-center gap-2 text-sm text-gray-400">
|
||||||
<span className="w-16">{label}:</span>
|
{icon}
|
||||||
{editingField === fieldName ? (
|
<span>{label}:</span>
|
||||||
<Input
|
</div>
|
||||||
type="datetime-local"
|
<div className="flex items-center">
|
||||||
value={tempValues[fieldName] || ''}
|
{editingField === fieldName ? (
|
||||||
onChange={(e) => setTempValues({ ...tempValues, [fieldName]: e.target.value })}
|
<Input
|
||||||
onBlur={() => handleFieldUpdate(fieldName, tempValues[fieldName])}
|
type="datetime-local"
|
||||||
onKeyDown={(e) => handleKeyPress(fieldName, e)}
|
value={tempValues[fieldName] || ''}
|
||||||
className="h-6 px-2 py-1 bg-gray-800 border-gray-600 text-white text-xs flex-1 min-w-0"
|
onChange={(e) => setTempValues({ ...tempValues, [fieldName]: e.target.value })}
|
||||||
autoFocus
|
onBlur={() => handleBlur(fieldName)}
|
||||||
data-no-navigate
|
onKeyDown={(e) => handleKeyPress(fieldName, e)}
|
||||||
/>
|
className="h-6 px-2 py-1 bg-gray-800 border-gray-600 text-white text-sm w-full"
|
||||||
) : (
|
autoFocus
|
||||||
<span
|
data-no-navigate
|
||||||
onClick={(e) => handleFieldClick(fieldName, value, e)}
|
/>
|
||||||
className="cursor-pointer hover:text-blue-400 flex-1 min-w-0 h-6 flex items-center"
|
) : (
|
||||||
title="Click to edit"
|
<span
|
||||||
data-no-navigate
|
onClick={(e) => handleClick(fieldName, value, e)}
|
||||||
>
|
className="cursor-pointer hover:text-blue-400 h-6 flex items-center text-white text-sm w-full"
|
||||||
{formatDateTime(value)}
|
title="Click to edit"
|
||||||
</span>
|
data-no-navigate
|
||||||
)}
|
>
|
||||||
</div>
|
{formatDateTime(value)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<div className="grid grid-cols-2 gap-x-4 gap-y-2">
|
<div className="grid grid-cols-4 gap-x-4 gap-y-2">
|
||||||
<div className="flex items-center gap-2 text-sm text-gray-400">
|
<div className="flex items-center gap-2 text-sm text-gray-400">
|
||||||
<Factory className="w-4 h-4" />
|
<Factory className="w-4 h-4" />
|
||||||
<span className="w-16">Job ID:</span>
|
<span>Job ID:</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
<span
|
<span
|
||||||
className="cursor-pointer hover:text-blue-400 transition-colors inline-flex items-center gap-1"
|
className="cursor-pointer hover:text-blue-400 transition-colors inline-flex items-center gap-1 text-sm text-white"
|
||||||
onClick={handleJobIdClick}
|
onClick={handleJobIdClick}
|
||||||
title="Click to copy job ID"
|
title="Click to copy job ID"
|
||||||
data-no-navigate
|
data-no-navigate
|
||||||
@@ -116,8 +144,10 @@ const JobCardDetails: React.FC<JobCardDetailsProps> = ({ job }) => {
|
|||||||
|
|
||||||
<div className="flex items-center gap-2 text-sm text-gray-400">
|
<div className="flex items-center gap-2 text-sm text-gray-400">
|
||||||
<Calendar className="w-4 h-4" />
|
<Calendar className="w-4 h-4" />
|
||||||
<span className="w-16">Created:</span>
|
<span>Created:</span>
|
||||||
<span>{formatDateTime(job.created)}</span>
|
</div>
|
||||||
|
<div className="text-sm text-white">
|
||||||
|
{formatDateTime(job.created)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DateField
|
<DateField
|
||||||
@@ -205,12 +235,14 @@ const PriceDisplay: React.FC<PriceDisplayProps> = ({ job }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-2 gap-4 text-sm text-gray-400">
|
<div className="grid grid-cols-4 gap-x-4 gap-y-2 text-sm text-gray-400">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Factory className="w-4 h-4" />
|
<Factory className="w-4 h-4" />
|
||||||
<span className="w-20">Target Price:</span>
|
<span>Target Price:</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
<span
|
<span
|
||||||
className="cursor-pointer hover:text-blue-400 transition-colors inline-flex items-center gap-1"
|
className="cursor-pointer hover:text-blue-400 transition-colors inline-flex items-center gap-1 text-white"
|
||||||
onClick={handleCopyTargetPrice}
|
onClick={handleCopyTargetPrice}
|
||||||
title="Click to copy target price per unit (based on projected revenue)"
|
title="Click to copy target price per unit (based on projected revenue)"
|
||||||
data-no-navigate
|
data-no-navigate
|
||||||
@@ -218,13 +250,20 @@ const PriceDisplay: React.FC<PriceDisplayProps> = ({ job }) => {
|
|||||||
{formatISK(targetPriceWithTax)}
|
{formatISK(targetPriceWithTax)}
|
||||||
{copying === 'targetPrice' && <Copy className="w-3 h-3 text-green-400" />}
|
{copying === 'targetPrice' && <Copy className="w-3 h-3 text-green-400" />}
|
||||||
</span>
|
</span>
|
||||||
|
{salesTax > 0 && (
|
||||||
|
<span className="text-xs text-gray-500 ml-1">
|
||||||
|
(+{(salesTax * 100).toFixed(1)}%)
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<DollarSign className="w-4 h-4" />
|
<DollarSign className="w-4 h-4" />
|
||||||
<span className="w-20">Break-even:</span>
|
<span>Break-even:</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
<span
|
<span
|
||||||
className="cursor-pointer hover:text-yellow-400 transition-colors inline-flex items-center gap-1"
|
className="cursor-pointer hover:text-yellow-400 transition-colors inline-flex items-center gap-1 text-white"
|
||||||
onClick={handleCopyBreakEvenPrice}
|
onClick={handleCopyBreakEvenPrice}
|
||||||
title="Click to copy break-even price per unit (based on actual costs)"
|
title="Click to copy break-even price per unit (based on actual costs)"
|
||||||
data-no-navigate
|
data-no-navigate
|
||||||
@@ -232,10 +271,11 @@ const PriceDisplay: React.FC<PriceDisplayProps> = ({ job }) => {
|
|||||||
{formatISK(breakEvenPriceWithTax)}
|
{formatISK(breakEvenPriceWithTax)}
|
||||||
{copying === 'breakEvenPrice' && <Copy className="w-3 h-3 text-green-400" />}
|
{copying === 'breakEvenPrice' && <Copy className="w-3 h-3 text-green-400" />}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
{salesTax > 0 && (
|
||||||
|
<span className="text-xs text-gray-500 ml-1">
|
||||||
<div className="col-span-2 text-xs text-gray-500">
|
(+{(salesTax * 100).toFixed(1)}%)
|
||||||
per unit {salesTax > 0 && `(+${(salesTax * 100).toFixed(1)}% tax)`}
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user