feat(inventory): inline site selector above items table

Items page now fetches all active sites and passes them alongside
preferredSiteId to ItemsTable. A "Working Site" row appears at the
top of the table — selecting a site calls setPreferredSite, revalidates
the page, and shows distances in the vendor sub-rows. A status hint
("Distances shown from selected site") appears when a site is active;
"No site selected — distances hidden" is the empty-state label.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Hardik 2026-05-15 00:54:54 +05:30
parent 79897c5b06
commit 4919b1d4e4
2 changed files with 54 additions and 12 deletions

View file

@ -1,9 +1,10 @@
"use client";
import { useState, useMemo } from "react";
import { useState, useMemo, useTransition } from "react";
import { Search, X, ChevronDown, ChevronRight, MapPin, Tag } from "lucide-react";
import { formatCurrency } from "@/lib/utils";
import { addToCart } from "@/lib/cart";
import { setPreferredSite } from "@/app/actions/site-preference";
type VendorOption = {
vendorId: string;
@ -25,11 +26,24 @@ function formatDist(km: number) {
return km < 1 ? `${Math.round(km * 1000)} m` : `${km.toFixed(0)} km`;
}
export function ItemsTable({ items, hasSite }: { items: CatalogItem[]; hasSite: boolean }) {
type SiteOption = { id: string; name: string; code: string };
export function ItemsTable({
items,
hasSite,
sites = [],
preferredSiteId = null,
}: {
items: CatalogItem[];
hasSite: boolean;
sites?: SiteOption[];
preferredSiteId?: string | null;
}) {
const [query, setQuery] = useState("");
const [expandedId, setExpandedId] = useState<string | null>(null);
const [sortBy, setSortBy] = useState<"distance" | "price">(hasSite ? "distance" : "price");
const [added, setAdded] = useState<Record<string, boolean>>({});
const [sitePending, startSiteTransition] = useTransition();
const filtered = useMemo(() => {
const q = query.toLowerCase().trim();
@ -79,6 +93,31 @@ export function ItemsTable({ items, hasSite }: { items: CatalogItem[]; hasSite:
return (
<div className="space-y-3">
{/* Site selector */}
{sites.length > 0 && (
<div className="flex items-center gap-3 rounded-lg border border-neutral-200 bg-white px-4 py-3">
<MapPin className="h-4 w-4 text-neutral-400 shrink-0" />
<label className="text-sm font-medium text-neutral-700 whitespace-nowrap">Working Site</label>
<select
value={preferredSiteId ?? ""}
disabled={sitePending}
onChange={(e) =>
startSiteTransition(() => setPreferredSite(e.target.value || null))
}
className="flex-1 max-w-xs rounded-lg border border-neutral-200 bg-neutral-50 px-3 py-1.5 text-sm text-neutral-700 focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-500/20 disabled:opacity-60"
>
<option value="">No site selected distances hidden</option>
{sites.map((s) => (
<option key={s.id} value={s.id}>{s.name} ({s.code})</option>
))}
</select>
{sitePending && <span className="text-xs text-neutral-400">Updating</span>}
{!sitePending && preferredSiteId && (
<span className="text-xs text-primary-600">Distances shown from selected site</span>
)}
</div>
)}
{/* Toolbar */}
<div className="flex items-center gap-3">
<div className="relative flex-1 max-w-sm">

View file

@ -11,7 +11,7 @@ export default async function InventoryItemsPage() {
const session = await auth();
if (!session?.user) redirect("/login");
const [user, products] = await Promise.all([
const [user, products, sites] = await Promise.all([
db.user.findUnique({
where: { id: session.user.id },
include: {
@ -33,6 +33,11 @@ export default async function InventoryItemsPage() {
},
orderBy: { name: "asc" },
}),
db.site.findMany({
where: { isActive: true },
orderBy: { name: "asc" },
select: { id: true, name: true, code: true },
}),
]);
const site = user?.preferredSite ?? null;
@ -61,16 +66,14 @@ export default async function InventoryItemsPage() {
<div className="max-w-6xl">
<div className="mb-6">
<h1 className="text-2xl font-semibold text-neutral-900">Browse Items</h1>
<p className="mt-1 text-sm text-neutral-500">
Search the catalogue and add items to your cart.
{site ? (
<span className="ml-1 text-primary-600">Distances shown from {site.name}.</span>
) : (
<span className="ml-1 text-neutral-400">Select a site in the header to enable distance sorting.</span>
)}
</p>
<p className="mt-1 text-sm text-neutral-500">Search the catalogue and add items to your cart.</p>
</div>
<ItemsTable items={items} hasSite={!!site} />
<ItemsTable
items={items}
hasSite={!!site}
sites={sites}
preferredSiteId={user?.preferredSiteId ?? null}
/>
</div>
);
}