Refactor: Improve price display and formatting
- Round prices to 4 significant digits. - Adjust grid layout for price display. - Move tax information to header.
This commit is contained in:
@@ -30,6 +30,13 @@ const JobCardDetails: React.FC<JobCardDetailsProps> = ({ job }) => {
|
|||||||
}).replace(',', '');
|
}).replace(',', '');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const roundToSignificantDigits = (num: number, digits: number = 4): number => {
|
||||||
|
if (num === 0) return 0;
|
||||||
|
const magnitude = Math.floor(Math.log10(Math.abs(num)));
|
||||||
|
const factor = Math.pow(10, digits - 1 - magnitude);
|
||||||
|
return Math.round(num * factor) / factor;
|
||||||
|
};
|
||||||
|
|
||||||
const handleFieldClick = (fieldName: string, currentValue: string | null, e: React.MouseEvent) => {
|
const handleFieldClick = (fieldName: string, currentValue: string | null, e: React.MouseEvent) => {
|
||||||
setEditingField(fieldName);
|
setEditingField(fieldName);
|
||||||
setTempValues({ ...tempValues, [fieldName]: currentValue || '' });
|
setTempValues({ ...tempValues, [fieldName]: currentValue || '' });
|
||||||
@@ -204,22 +211,29 @@ const PriceDisplay: React.FC<PriceDisplayProps> = ({ job }) => {
|
|||||||
window.addEventListener('storage', handleStorageChange);
|
window.addEventListener('storage', handleStorageChange);
|
||||||
return () => window.removeEventListener('storage', handleStorageChange);
|
return () => window.removeEventListener('storage', handleStorageChange);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const roundToSignificantDigits = (num: number, digits: number = 4): number => {
|
||||||
|
if (num === 0) return 0;
|
||||||
|
const magnitude = Math.floor(Math.log10(Math.abs(num)));
|
||||||
|
const factor = Math.pow(10, digits - 1 - magnitude);
|
||||||
|
return Math.round(num * factor) / factor;
|
||||||
|
};
|
||||||
|
|
||||||
// Calculate total costs
|
// Calculate total costs
|
||||||
const totalCosts = job.expenditures?.reduce((sum, tx) => sum + tx.totalPrice, 0) || 0;
|
const totalCosts = job.expenditures?.reduce((sum, tx) => sum + tx.totalPrice, 0) || 0;
|
||||||
|
|
||||||
// Target price (based on projected revenue)
|
// Target price (based on projected revenue)
|
||||||
const targetPricePerUnit = job.projectedRevenue / job.produced;
|
const targetPricePerUnit = job.projectedRevenue / job.produced;
|
||||||
const targetPriceWithTax = targetPricePerUnit * (1 + salesTax);
|
const targetPriceWithTax = roundToSignificantDigits(targetPricePerUnit * (1 + salesTax));
|
||||||
|
|
||||||
// Break-even price (based on actual costs)
|
// Break-even price (based on actual costs)
|
||||||
const breakEvenPricePerUnit = totalCosts / job.produced;
|
const breakEvenPricePerUnit = totalCosts / job.produced;
|
||||||
const breakEvenPriceWithTax = breakEvenPricePerUnit * (1 + salesTax);
|
const breakEvenPriceWithTax = roundToSignificantDigits(breakEvenPricePerUnit * (1 + salesTax));
|
||||||
|
|
||||||
const handleCopyTargetPrice = async (e: React.MouseEvent) => {
|
const handleCopyTargetPrice = async (e: React.MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
await copyToClipboard(
|
await copyToClipboard(
|
||||||
targetPriceWithTax.toFixed(2),
|
targetPriceWithTax.toString(),
|
||||||
'targetPrice',
|
'targetPrice',
|
||||||
'Target price copied to clipboard'
|
'Target price copied to clipboard'
|
||||||
);
|
);
|
||||||
@@ -228,19 +242,21 @@ const PriceDisplay: React.FC<PriceDisplayProps> = ({ job }) => {
|
|||||||
const handleCopyBreakEvenPrice = async (e: React.MouseEvent) => {
|
const handleCopyBreakEvenPrice = async (e: React.MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
await copyToClipboard(
|
await copyToClipboard(
|
||||||
breakEvenPriceWithTax.toFixed(2),
|
breakEvenPriceWithTax.toString(),
|
||||||
'breakEvenPrice',
|
'breakEvenPrice',
|
||||||
'Break-even price copied to clipboard'
|
'Break-even price copied to clipboard'
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const taxSuffix = salesTax > 0 ? ` (+${(salesTax * 100).toFixed(1)}% tax)` : '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid gap-x-4 gap-y-2 text-sm text-gray-400" style={{ gridTemplateColumns: 'auto 1fr auto 1fr' }}>
|
<div className="grid gap-x-4 gap-y-2 text-sm" style={{ gridTemplateColumns: '1fr 1fr 1fr 1fr' }}>
|
||||||
<div className="flex items-center gap-2">
|
<div className="col-span-2 flex items-center gap-2 text-gray-400">
|
||||||
<Factory className="w-4 h-4" />
|
<Factory className="w-4 h-4" />
|
||||||
<span>Target Price:</span>
|
<span>Target Price{taxSuffix}:</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1">
|
<div className="col-span-2 flex items-center gap-1">
|
||||||
<span
|
<span
|
||||||
className="cursor-pointer hover:text-blue-400 transition-colors inline-flex items-center gap-1 text-white"
|
className="cursor-pointer hover:text-blue-400 transition-colors inline-flex items-center gap-1 text-white"
|
||||||
onClick={handleCopyTargetPrice}
|
onClick={handleCopyTargetPrice}
|
||||||
@@ -250,18 +266,13 @@ 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="col-span-2 flex items-center gap-2 text-gray-400">
|
||||||
<DollarSign className="w-4 h-4" />
|
<DollarSign className="w-4 h-4" />
|
||||||
<span>Break-even:</span>
|
<span>Break-even{taxSuffix}:</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1">
|
<div className="col-span-2 flex items-center gap-1">
|
||||||
<span
|
<span
|
||||||
className="cursor-pointer hover:text-yellow-400 transition-colors inline-flex items-center gap-1 text-white"
|
className="cursor-pointer hover:text-yellow-400 transition-colors inline-flex items-center gap-1 text-white"
|
||||||
onClick={handleCopyBreakEvenPrice}
|
onClick={handleCopyBreakEvenPrice}
|
||||||
@@ -271,11 +282,6 @@ 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>
|
||||||
{salesTax > 0 && (
|
|
||||||
<span className="text-xs text-gray-500 ml-1">
|
|
||||||
(+{(salesTax * 100).toFixed(1)}%)
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user