diff --git a/frontend/src/lib/quoteUtils.ts b/frontend/src/lib/quoteUtils.ts index 003eb3a..647f184 100644 --- a/frontend/src/lib/quoteUtils.ts +++ b/frontend/src/lib/quoteUtils.ts @@ -40,6 +40,22 @@ export function specsLabel(specs: string[]): string { return specs.join(" · "); } +// نام نمایشی متریال — نام سراسری واقعی (materials.name)، نه دسته. +// برند و مشخصه فقط وقتی به نام اضافه می‌شوند که داخلش نیامده باشند (بدون تکرار). +export function materialLabel(m: Pick): string { + const name = (m.name || "").trim(); + const specs = (m.specs || []).filter(Boolean); + if (!name && !m.brand && !specs.length) return m.category === "؟" ? "؟" : "کالا"; + if (!name) { + return [m.brand, ...specs].filter(Boolean).join(" ") || "کالا"; + } + const parts = [name]; + if (m.brand && !name.includes(m.brand)) parts.push(m.brand); + const missing = specs.filter((s) => !name.includes(s)); + if (missing.length) parts.push(`(${missing.join("، ")})`); + return parts.join(" "); +} + // آرایه‌ی termsheet_ids یک قلم (هر دو فرمت قدیم/جدید) export function itemTermsheetIds(li: LineItem): string[] { if (li.termsheet_ids) return li.termsheet_ids; @@ -157,8 +173,7 @@ export function projectCell(q: Quote): string { export function itemsSummary(store: Store, q: Quote): { text: string; count: number } { const items = activeItems(q).map((li) => { const m = materialById(store.materials, li.material_id); - const specs = materialSpecs(m); - return `${m.category} ${m.brand}${specs.length ? " " + specs.join(" · ") : ""}`; + return materialLabel(m); }); return { text: items.join("، "), count: items.length }; } diff --git a/frontend/src/pages/Dashboard/QuoteCard.tsx b/frontend/src/pages/Dashboard/QuoteCard.tsx index a7157a0..1b1575f 100644 --- a/frontend/src/pages/Dashboard/QuoteCard.tsx +++ b/frontend/src/pages/Dashboard/QuoteCard.tsx @@ -5,7 +5,7 @@ import { isExpired, itemsSummary, materialById, - materialSpecs, + materialLabel, projectCell, quoteTotal, supplierById, @@ -165,11 +165,10 @@ export function QuoteCard({ store, q, expanded, onToggleExpand, onAccept, onEdit
{store.quotes.find((x) => x.id === q.id)?.line_items?.filter((li) => !li.rejected).map((li) => { const m = materialById(store.materials, li.material_id); - const specs = materialSpecs(m); return ( - {m.category} {m.brand}{specs.length ? ` · ${specs.join(" · ")}` : ""} + {materialLabel(m)} {fmtNum(li.quantity)} {li.unit} diff --git a/frontend/src/pages/Projects/ProjectsPage.tsx b/frontend/src/pages/Projects/ProjectsPage.tsx index ef296d3..c75175f 100644 --- a/frontend/src/pages/Projects/ProjectsPage.tsx +++ b/frontend/src/pages/Projects/ProjectsPage.tsx @@ -19,7 +19,7 @@ import { activeItems, lineItemTotal, materialById, - materialSpecs, + materialLabel, refConverted, supplierById, } from "../../lib/quoteUtils"; @@ -154,9 +154,7 @@ function renderProjectDetail(store: Store, name: string): React.ReactElement { const matName = (mid: string) => { const m = materialById(store.materials, mid); - if (m.category === "؟") return "؟"; - const specs = materialSpecs(m); - return `${m.category} ${m.brand}${specs.length ? " · " + specs.join(" · ") : ""}`; + return materialLabel(m); }; const supName = (sid: string) => { diff --git a/frontend/src/pages/Suppliers/CategoryPage.tsx b/frontend/src/pages/Suppliers/CategoryPage.tsx index 8bcf771..21c3f74 100644 --- a/frontend/src/pages/Suppliers/CategoryPage.tsx +++ b/frontend/src/pages/Suppliers/CategoryPage.tsx @@ -11,6 +11,7 @@ import { Header, PageLayout } from "../../components/layout/Header"; import { EmptyState } from "../../components/EmptyState"; import { Combobox } from "../../components/Combobox"; import { SupplierCard, suppliersOfCategory } from "./supplierUtils"; +import { materialLabel } from "../../lib/quoteUtils"; import { Button } from "../../components/ui/button"; // صفحه‌ی تأمین‌کننده‌های یک دسته + فرم افزودن تأمین‌کننده @@ -53,7 +54,7 @@ export function CategoryPage() { if (li.rejected) return; const m = materials.find((x) => x.id === li.material_id); if (!m) return; - const label = `${m.category}${m.brand ? " " + m.brand : ""}${m.specs.length ? " (" + m.specs.join("، ") + ")" : ""}`; + const label = materialLabel(m); if (!map.has(q.supplier_id)) map.set(q.supplier_id, []); const list = map.get(q.supplier_id)!; if (!list.includes(label)) list.push(label); diff --git a/frontend/src/pages/Suppliers/PurchaseHistory.tsx b/frontend/src/pages/Suppliers/PurchaseHistory.tsx index 334af5c..b2bc4be 100644 --- a/frontend/src/pages/Suppliers/PurchaseHistory.tsx +++ b/frontend/src/pages/Suppliers/PurchaseHistory.tsx @@ -3,7 +3,7 @@ import { Link } from "react-router-dom"; import { Building2, Paperclip, Plus } from "lucide-react"; import type { Attachment, Material, Quote, Supplier } from "../../lib/types"; import { api } from "../../lib/api"; -import { activeItems, materialById, materialSpecs, quoteTotal, specsLabel } from "../../lib/quoteUtils"; +import { activeItems, materialById, materialLabel, quoteTotal } from "../../lib/quoteUtils"; import { fmtDate, faNum } from "../../lib/formatters"; import { groupCommas } from "../../lib/numutil"; import { STATUS_LABEL } from "../../lib/constants"; @@ -85,8 +85,7 @@ export function PurchaseHistory({ supplier, quotes, materials }: PurchaseHistory {purchases.map((q) => { const items = activeItems(q).map((li) => { const m = materialById(materials, li.material_id); - const specs = materialSpecs(m); - return `${m.category} ${m.brand}${specs.length ? " " + specsLabel(specs) : ""}`.trim(); + return materialLabel(m); }); const total = quoteTotal(q); const attach = attachments[q.id]; diff --git a/frontend/src/pages/Units/UnitsPage.tsx b/frontend/src/pages/Units/UnitsPage.tsx index 145417e..89e45bb 100644 --- a/frontend/src/pages/Units/UnitsPage.tsx +++ b/frontend/src/pages/Units/UnitsPage.tsx @@ -331,7 +331,7 @@ export function UnitsPage() { return (
-
+
{!selectedTopic ? (