Skip to contentVibraUI

Details

The product's drawing and what the catalogue holds for it — SKU, category, supplier, reorder point and status — with how many orders hold it and when it first sold; reads productRecord(id).

Preview

Install

npx shadcn@latest add @vibra/widget-ecommerce-product-product-details

Needs the @vibra registry in your components.json — set it up once.

Source

app/ecommerce/products/[id]/components/product-details.tsx
import { PRODUCT_STATUS_LABELS, PRODUCT_STATUS_MAP } from "@/lib/dashboards/ecommerce/vocabulary"
import { formatDate, formatNumber } from "@/lib/format"
import { type Product } from "@/lib/sample-data"
import { DescriptionList } from "@/components/ui/description-list"
import { ProductArt } from "@/components/ui/product-art"
import { StatusBadge } from "@/components/ui/status-badge"
import { Widget } from "@/components/ui/widget"

export type ProductDetailsProps = {
  product: Product
  orderCount: number
  /** When the oldest sale of it was placed; absent for a product nobody has bought and kept. */
  firstSold?: Date
}

/**
 * The product's drawing, the facts the catalogue row holds, and two the order
 * book adds to them: how many orders hold it, and when it first sold.
 */
export function ProductDetails({ product, orderCount, firstSold }: ProductDetailsProps) {
  return (
    <Widget data-widget="widget-ecommerce-product-product-details" title="Details" description="What the catalogue holds for it" className="h-full">
      {/* The drawing heads the facts in a narrow card and stands beside them in a wide one. */}
      <div className="@container/details">
        <div className="flex flex-col gap-4 @lg/details:flex-row @lg/details:items-start @lg/details:gap-6">
          <ProductArt
            name={product.name}
            category={product.category}
            className="w-20 shrink-0 rounded-md @lg/details:w-32"
          />
          <DescriptionList
            className="min-w-0 flex-1"
            items={[
              { term: "SKU", description: <span className="font-mono text-xs">{product.sku}</span> },
              { term: "Category", description: product.category },
              { term: "Supplier", description: product.supplier },
              {
                term: "Reorder point",
                description: <span className="tabular-nums">{formatNumber(product.reorderPoint)} units</span>,
              },
              {
                term: "Status",
                description: (
                  <StatusBadge
                    status={product.status}
                    map={PRODUCT_STATUS_MAP}
                    label={PRODUCT_STATUS_LABELS[product.status]}
                  />
                ),
              },
              { term: "Orders", description: <span className="tabular-nums">{formatNumber(orderCount)}</span> },
              {
                term: "First sold",
                description: firstSold ? formatDate(firstSold, "medium", { timeZone: "UTC" }) : "Not yet",
              },
            ]}
          />
        </div>
      </div>
    </Widget>
  )
}

Its page

On its page the card sits among the rest of the dashboard and shares its range and its data with them.

From the Product record page