Files
ember-market-frontend/components/forms/pricing-tiers.tsx
g fe01f31538
Some checks failed
Build Frontend / build (push) Failing after 7s
Refactor UI imports and update component paths
Replaces imports from 'components/ui' with 'components/common' across the app and dashboard pages, and updates model and API imports to use new paths under 'lib'. Removes redundant authentication checks from several dashboard pages. Adds new dashboard components and utility files, and reorganizes hooks and services into the 'lib' directory for improved structure.
2026-01-13 05:02:13 +00:00

141 lines
4.4 KiB
TypeScript

"use client";
import { Button } from "@/components/common/button";
import { Input } from "@/components/common/input";
import { Trash, PlusCircle } from "lucide-react";
interface PricingTiersProps {
pricing: any[];
handleTierChange: (
e: React.ChangeEvent<HTMLInputElement>,
index: number
) => void;
handleRemoveTier: (index: number) => void;
handleAddTier: () => void;
}
export const PricingTiers = ({
pricing = [],
handleTierChange,
handleRemoveTier,
handleAddTier,
}: PricingTiersProps) => {
const formatNumber = (num: number) => {
if (isNaN(num) || num === null || num === undefined) return "";
// Return the number as-is without any formatting to prevent precision issues
return num.toString();
};
const formatTotal = (num: number) => {
return num.toFixed(2);
};
const calculateTotal = (quantity: number, pricePerUnit: number) => {
return formatTotal(quantity * pricePerUnit);
};
const handleTotalChange = (
e: React.ChangeEvent<HTMLInputElement>,
index: number,
minQuantity: number
) => {
if (!handleTierChange || !e || !e.target) return;
const totalPrice = Number(e.target.value);
const pricePerUnit = minQuantity > 0 ? totalPrice / minQuantity : 0;
// Create a simple synthetic event with the raw number
const syntheticEvent = {
target: {
name: 'pricePerUnit',
value: pricePerUnit.toString()
}
} as React.ChangeEvent<HTMLInputElement>;
try {
handleTierChange(syntheticEvent, index);
} catch (error) {
console.error('Error in handleTotalChange:', error);
}
};
return (
<div>
<h3 className="text-sm font-medium">Tiered Pricing</h3>
{pricing?.length > 0 ? (
<>
<div className="grid grid-cols-[1fr_1fr_1fr_auto] gap-2 mt-2 mb-1">
<div className="text-xs text-muted-foreground">Quantity</div>
<div className="text-xs text-muted-foreground">Price Per Unit</div>
<div className="text-xs text-muted-foreground">Total Price</div>
<div className="w-8" />
</div>
{[...pricing]
.sort((a, b) => (a?.minQuantity || 0) - (b?.minQuantity || 0))
.map((tier, sortedIndex) => {
if (!tier) return null;
// Prefer stable identifiers for mapping back to the original array
const keyId = tier._id || tier.tempId;
const originalIndex = pricing.findIndex((p) => (p?._id || p?.tempId) === keyId);
return (
<div
key={keyId || originalIndex}
className="grid grid-cols-[1fr_1fr_1fr_auto] gap-2 mt-2"
>
<Input
name="minQuantity"
type="number"
placeholder="Min Quantity"
value={tier?.minQuantity === 0 ? "" : (tier?.minQuantity || "")}
onChange={(e) => handleTierChange?.(e, originalIndex)}
className="h-8 text-sm px-2"
/>
<Input
name="pricePerUnit"
type="number"
placeholder="Price per unit"
value={tier?.pricePerUnit === 0 ? "" : formatNumber(tier?.pricePerUnit || 0)}
onChange={(e) => handleTierChange?.(e, originalIndex)}
className="h-8 text-sm px-2"
/>
<Input
type="number"
placeholder="Total price"
value={
tier?.minQuantity && tier?.pricePerUnit
? calculateTotal(tier.minQuantity, tier.pricePerUnit)
: ""
}
onChange={(e) => handleTotalChange(e, originalIndex, tier?.minQuantity || 0)}
className="h-8 text-sm px-2"
/>
<Button
variant="ghost"
size="icon"
className="text-red-500 hover:bg-red-100"
onClick={() => handleRemoveTier?.(originalIndex)}
>
<Trash className="h-5 w-5" />
</Button>
</div>
);
})}
</>
) : (
<p className="text-sm text-gray-500 mt-2">No pricing tiers added.</p>
)}
<Button variant="outline" size="sm" className="mt-2" onClick={() => handleAddTier?.()}>
<PlusCircle className="w-4 h-4 mr-1" />
Add Tier
</Button>
</div>
);
};