Enhance PriceDisplay component with adjusted pricing calculations
- Added calculations for adjusted target and break-even prices based on remaining revenue and uncovered costs. - Implemented clipboard copy functionality for adjusted prices. - Updated layout to display adjusted prices alongside original calculations for better clarity.
This commit is contained in:
@@ -219,17 +219,29 @@ const PriceDisplay: React.FC<PriceDisplayProps> = ({ job }) => {
|
|||||||
return Math.round(num * factor) / factor;
|
return Math.round(num * factor) / factor;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate total costs
|
// Calculate total costs and income
|
||||||
const totalCosts = job.expenditures?.reduce((sum, tx) => sum + tx.totalPrice, 0) || 0;
|
const totalCosts = job.expenditures?.reduce((sum, tx) => sum + tx.totalPrice, 0) || 0;
|
||||||
|
const totalIncome = job.income?.reduce((sum, tx) => sum + tx.totalPrice, 0) || 0;
|
||||||
|
const itemsSold = job.income?.reduce((sum, tx) => sum + tx.quantity, 0) || 0;
|
||||||
|
const itemsRemaining = (job.produced || 0) - itemsSold;
|
||||||
|
|
||||||
// Target price (based on projected revenue)
|
// Original calculations (based on full revenue and costs)
|
||||||
const targetPricePerUnit = job.projectedRevenue / job.produced;
|
const targetPricePerUnit = job.projectedRevenue / job.produced;
|
||||||
const targetPriceWithTax = roundToSignificantDigits(targetPricePerUnit * (1 + salesTax));
|
const targetPriceWithTax = roundToSignificantDigits(targetPricePerUnit * (1 + salesTax));
|
||||||
|
|
||||||
// Break-even price (based on actual costs)
|
|
||||||
const breakEvenPricePerUnit = totalCosts / job.produced;
|
const breakEvenPricePerUnit = totalCosts / job.produced;
|
||||||
const breakEvenPriceWithTax = roundToSignificantDigits(breakEvenPricePerUnit * (1 + salesTax));
|
const breakEvenPriceWithTax = roundToSignificantDigits(breakEvenPricePerUnit * (1 + salesTax));
|
||||||
|
|
||||||
|
// Adjusted calculations (based on remaining revenue and uncovered costs)
|
||||||
|
const remainingRevenue = job.projectedRevenue - totalIncome;
|
||||||
|
const uncoveredCosts = totalCosts - totalIncome;
|
||||||
|
|
||||||
|
const adjustedTargetPricePerUnit = itemsRemaining > 0 ? remainingRevenue / itemsRemaining : 0;
|
||||||
|
const adjustedTargetPriceWithTax = roundToSignificantDigits(adjustedTargetPricePerUnit * (1 + salesTax));
|
||||||
|
|
||||||
|
const adjustedBreakEvenPricePerUnit = itemsRemaining > 0 ? Math.max(0, uncoveredCosts / itemsRemaining) : 0;
|
||||||
|
const adjustedBreakEvenPriceWithTax = roundToSignificantDigits(adjustedBreakEvenPricePerUnit * (1 + salesTax));
|
||||||
|
|
||||||
const handleCopyTargetPrice = async (e: React.MouseEvent) => {
|
const handleCopyTargetPrice = async (e: React.MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
await copyToClipboard(
|
await copyToClipboard(
|
||||||
@@ -248,6 +260,24 @@ const PriceDisplay: React.FC<PriceDisplayProps> = ({ job }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleCopyAdjustedTargetPrice = async (e: React.MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
await copyToClipboard(
|
||||||
|
adjustedTargetPriceWithTax.toString(),
|
||||||
|
'adjustedTargetPrice',
|
||||||
|
'Adjusted target price copied to clipboard'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCopyAdjustedBreakEvenPrice = async (e: React.MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
await copyToClipboard(
|
||||||
|
adjustedBreakEvenPriceWithTax.toString(),
|
||||||
|
'adjustedBreakEvenPrice',
|
||||||
|
'Adjusted break-even price copied to clipboard'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const taxSuffix = salesTax > 0 ? ` (+${(salesTax * 100).toFixed(1)}% tax)` : '';
|
const taxSuffix = salesTax > 0 ? ` (+${(salesTax * 100).toFixed(1)}% tax)` : '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -283,6 +313,38 @@ const PriceDisplay: React.FC<PriceDisplayProps> = ({ job }) => {
|
|||||||
{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>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2 text-gray-400">
|
||||||
|
<Factory className="w-4 h-4" />
|
||||||
|
<span>Adjusted Target{taxSuffix}:</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 text-gray-400">
|
||||||
|
<DollarSign className="w-4 h-4" />
|
||||||
|
<span>Adjusted Break-even{taxSuffix}:</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-1 text-lg">
|
||||||
|
<span
|
||||||
|
className="cursor-pointer hover:text-blue-400 transition-colors inline-flex items-center gap-1 text-white"
|
||||||
|
onClick={handleCopyAdjustedTargetPrice}
|
||||||
|
title="Click to copy adjusted target price per unit (based on remaining revenue)"
|
||||||
|
data-no-navigate
|
||||||
|
>
|
||||||
|
{formatISK(adjustedTargetPriceWithTax)}
|
||||||
|
{copying === 'adjustedTargetPrice' && <Copy className="w-3 h-3 text-green-400" />}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1 text-lg">
|
||||||
|
<span
|
||||||
|
className="cursor-pointer hover:text-yellow-400 transition-colors inline-flex items-center gap-1 text-white"
|
||||||
|
onClick={handleCopyAdjustedBreakEvenPrice}
|
||||||
|
title="Click to copy adjusted break-even price per unit (based on uncovered costs)"
|
||||||
|
data-no-navigate
|
||||||
|
>
|
||||||
|
{formatISK(adjustedBreakEvenPriceWithTax)}
|
||||||
|
{copying === 'adjustedBreakEvenPrice' && <Copy className="w-3 h-3 text-green-400" />}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user