remove PriceListSheet component and integrate new price list creation page

This commit is contained in:
2026-09-09 16:31:35 +05:30
parent eb0303fa3f
commit 617c0713a1
12 changed files with 1145 additions and 288 deletions
@@ -26,6 +26,7 @@ import {
rides as ridesApi,
items as itemsApi,
users as usersApi,
priceLists as priceListsApi,
getApiError,
getPaginatedItems,
} from '@/lib/api/index'
@@ -83,6 +84,8 @@ export default function NewSalePage() {
})
const watchedItems = useWatch({ control: form.control, name: 'items' })
const watchedStoreId = useWatch({ control: form.control, name: 'storeId' })
const watchedSaleDate = useWatch({ control: form.control, name: 'saleDate' })
const itemsById = useMemo(
() => new Map(itemsList.map((item) => [item.id, item])),
[itemsList]
@@ -91,6 +94,38 @@ export default function NewSalePage() {
() => watchedItems.reduce((sum, item) => sum + (Number(item.quantity) || 0) * (Number(item.unitPrice) || 0), 0),
[watchedItems]
)
const [priceMinByIndex, setPriceMinByIndex] = useState<Record<number, number | null>>({})
const [priceRetailByIndex, setPriceRetailByIndex] = useState<Record<number, number | null>>({})
async function resolveAndSetPrice(index: number, itemId: string) {
const item = itemsById.get(itemId)
if (watchedStoreId && watchedSaleDate) {
try {
const resolution = await priceListsApi.resolve({ itemId, storeId: watchedStoreId, saleDate: watchedSaleDate })
form.setValue(`items.${index}.unitPrice`, resolution.price, { shouldValidate: true })
setPriceMinByIndex((prev) => ({ ...prev, [index]: resolution.minPrice }))
setPriceRetailByIndex((prev) => ({ ...prev, [index]: resolution.retailPrice }))
return
} catch {
// No resolvable price for this store/date (e.g. store/item not found) — fall back below.
}
}
if (item) {
form.setValue(`items.${index}.unitPrice`, item.salePrice, { shouldValidate: true })
setPriceMinByIndex((prev) => ({ ...prev, [index]: null }))
setPriceRetailByIndex((prev) => ({ ...prev, [index]: null }))
}
}
// Re-resolve every already-selected line's price whenever the store or sale date changes,
// since the applicable price list is scoped by the store's region and the sale date.
useEffect(() => {
if (!watchedStoreId || !watchedSaleDate) return
watchedItems.forEach((line, index) => {
if (line.itemId) resolveAndSetPrice(index, line.itemId)
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [watchedStoreId, watchedSaleDate])
useEffect(() => {
Promise.all([
@@ -291,10 +326,7 @@ export default function NewSalePage() {
value={field.value as string}
onValueChange={(value) => {
field.onChange(value)
const nextItem = value ? itemsById.get(value) : undefined
if (nextItem) {
form.setValue(`items.${index}.unitPrice`, nextItem.salePrice, { shouldValidate: true })
}
if (value) resolveAndSetPrice(index, value)
}}
>
<SelectTrigger>
@@ -359,6 +391,16 @@ export default function NewSalePage() {
/>
)}
/>
{priceMinByIndex[index] != null && (
<p className="mt-1 text-xs text-muted-foreground">
Negotiable down to {fmtAmount(priceMinByIndex[index]!)}
</p>
)}
{priceRetailByIndex[index] != null && (
<p className="mt-1 text-xs text-muted-foreground">
Retail price: {fmtAmount(priceRetailByIndex[index]!)}
</p>
)}
</td>
<td className="px-3 py-2 text-right font-medium">{fmtAmount(lineTotal)}</td>