style(quotes): stack company under customer in the same column

Customer and Company used to be two side-by-side DataTable columns,
which cost ~120 px of horizontal width and made rows feel sparse when
the operator is just eyeballing who's-who across many quotes.

Now the Customer column carries both names in one cell: customer in
the default font-medium row, company underneath in text-xs
text-muted-foreground so the visual hierarchy reads as
"Primary person  >  Their organisation".

Effect on row layout:
- Total table column count drops from 7 to 6.
- Row width shrinks on desktop, freeing space for the larger
  Quote # column (which carries the follow-up / replied / aged
  chips and was getting crowded).
- The mobile card render (used below the sm breakpoint) gets the
  same stacking so the company line shows on every viewport.

Edge cases handled:
- Both names empty             → '—' muted placeholder
- Customer present, no company → just the customer name
- Company present, no customer → '—' for the customer, company below
- Very long names              → truncate via className='truncate'

The existing standalone Company column is removed; downstream
code (DataTable, mobileCardRender) reads from the same expanded
fields so no extra fetches.
This commit is contained in:
SJCoetz 2026-07-27 00:04:03 +02:00
parent e626292b04
commit 0ad9c42df4

View file

@ -410,14 +410,31 @@ export default function QuotesPage() {
),
},
{
// Customer column also carries the Company underneath as a
// small muted sub-line — collapses the row width by ~120 px
// and makes the list easier to scan when the operator is
// visual-eyeballing who's-who across many rows.
key: "customer",
label: "Customer",
render: (item: Quote) => <span>{item.expand?.customer?.customer || "—"}</span>,
},
{
key: "company",
label: "Company",
render: (item: Quote) => <span>{item.expand?.company?.company || "—"}</span>,
render: (item: Quote) => {
const customerName = item.expand?.customer?.customer;
const companyName = item.expand?.company?.company;
if (!customerName && !companyName) {
return <span className="text-muted-foreground">—</span>;
}
return (
<div className="flex flex-col gap-0.5 leading-tight">
{customerName ? (
<span className="font-medium">{customerName}</span>
) : (
<span className="text-muted-foreground">—</span>
)}
{companyName ? (
<span className="text-xs text-muted-foreground">{companyName}</span>
) : null}
</div>
);
},
},
{
key: "rep",
@ -606,9 +623,16 @@ export default function QuotesPage() {
{hasCustomerReply(item) && <RepliedChip repliedAt={item.last_reply_at} />}
{isAged(item) && <AgedBadge created={item.created} />}
</div>
<p className="text-sm text-muted-foreground truncate">
{item.expand?.customer?.customer || "—"}
</p>
<div className="leading-tight">
<p className="text-sm font-medium truncate">
{item.expand?.customer?.customer || "—"}
</p>
{item.expand?.company?.company ? (
<p className="text-xs text-muted-foreground truncate">
{item.expand.company.company}
</p>
) : null}
</div>
</div>
</div>
<Badge variant="outline" className={quoteStatusClassName(item.status)}>