feat: update inventory columns to match legacy portal and grant admins all companies
Some checks failed
Build and Deploy / build (push) Successful in 4m57s
Build and Deploy / deploy (push) Failing after 13s

Inventory summary table now shows Plant, Cust Part#, Vorteq Part#,
Vorteq Desc, Warehouse, Qty LB, Rows with a blue View button for
drill-down. Admins see all active companies in the company selector.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lorentz 2026-02-16 17:02:20 +00:00
parent cf9af94f7d
commit 87c1cdf8da
3 changed files with 45 additions and 24 deletions

View file

@ -12,7 +12,7 @@ import {
} from '@/components/ui/table'; } from '@/components/ui/table';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Search, Download } from 'lucide-react'; import { Search, Download, Eye } from 'lucide-react';
import { InventorySummaryRow } from '@/services/inventory'; import { InventorySummaryRow } from '@/services/inventory';
type InventorySummaryTableProps = { type InventorySummaryTableProps = {
@ -30,31 +30,34 @@ export function InventorySummaryTable({
(row) => (row) =>
row.part_num.toLowerCase().includes(searchTerm.toLowerCase()) || row.part_num.toLowerCase().includes(searchTerm.toLowerCase()) ||
row.description?.toLowerCase().includes(searchTerm.toLowerCase()) || row.description?.toLowerCase().includes(searchTerm.toLowerCase()) ||
row.plant?.toLowerCase().includes(searchTerm.toLowerCase()) row.plant?.toLowerCase().includes(searchTerm.toLowerCase()) ||
row.cust_part_num?.toLowerCase().includes(searchTerm.toLowerCase())
); );
const handleExportCSV = () => { const handleExportCSV = () => {
// Create CSV content
const headers = [ const headers = [
'Part Number',
'Description',
'Plant', 'Plant',
'Cust Part#',
'Vorteq Part#',
'Vorteq Desc',
'Warehouse', 'Warehouse',
'On Hand Qty', 'Qty LB',
'Rows',
]; ];
const rows = filteredData.map((row) => [ const rows = filteredData.map((row) => [
row.plant,
row.cust_part_num || '',
row.part_num, row.part_num,
row.description || '', row.description || '',
row.plant,
row.warehouse, row.warehouse,
row.on_hand_qty, row.on_hand_qty,
row.rows,
]); ]);
const csvContent = [headers, ...rows] const csvContent = [headers, ...rows]
.map((row) => row.join(',')) .map((row) => row.map((cell) => `"${cell}"`).join(','))
.join('\n'); .join('\n');
// Download CSV
const blob = new Blob([csvContent], { type: 'text/csv' }); const blob = new Blob([csvContent], { type: 'text/csv' });
const url = window.URL.createObjectURL(blob); const url = window.URL.createObjectURL(blob);
const a = document.createElement('a'); const a = document.createElement('a');
@ -95,32 +98,39 @@ export function InventorySummaryTable({
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
<TableHead>Part Number</TableHead>
<TableHead>Description</TableHead>
<TableHead>Plant</TableHead> <TableHead>Plant</TableHead>
<TableHead>Cust Part#</TableHead>
<TableHead>Vorteq Part#</TableHead>
<TableHead>Vorteq Desc</TableHead>
<TableHead>Warehouse</TableHead> <TableHead>Warehouse</TableHead>
<TableHead className="text-right">On Hand Qty</TableHead> <TableHead className="text-right">Qty LB</TableHead>
<TableHead className="text-right">Rows</TableHead>
<TableHead className="w-[70px]"></TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{filteredData.map((row, idx) => ( {filteredData.map((row, idx) => (
<TableRow key={idx}> <TableRow key={idx}>
<TableCell>
<Link
href={`/inventory/${category}/detail?part=${encodeURIComponent(row.part_num)}&plant=${encodeURIComponent(row.plant_key || row.plant)}&warehouse=${encodeURIComponent(row.warehouse)}`}
className="font-medium hover:underline"
>
{row.part_num}
</Link>
</TableCell>
<TableCell className="max-w-xs truncate">
{row.description || 'N/A'}
</TableCell>
<TableCell>{row.plant}</TableCell> <TableCell>{row.plant}</TableCell>
<TableCell>{row.cust_part_num || '-'}</TableCell>
<TableCell className="font-medium">{row.part_num}</TableCell>
<TableCell className="max-w-xs truncate">
{row.description || '-'}
</TableCell>
<TableCell>{row.warehouse}</TableCell> <TableCell>{row.warehouse}</TableCell>
<TableCell className="text-right"> <TableCell className="text-right">
{Number(row.on_hand_qty).toLocaleString()} {Number(row.on_hand_qty).toLocaleString()}
</TableCell> </TableCell>
<TableCell className="text-right">{row.rows}</TableCell>
<TableCell>
<Link
href={`/inventory/${category}/detail?part=${encodeURIComponent(row.part_num)}&plant=${encodeURIComponent(row.plant_key || row.plant)}&warehouse=${encodeURIComponent(row.warehouse)}`}
>
<Button variant="ghost" size="icon" className="h-8 w-8 text-blue-600 hover:text-blue-800">
<Eye className="h-4 w-4" />
</Button>
</Link>
</TableCell>
</TableRow> </TableRow>
))} ))}
</TableBody> </TableBody>

View file

@ -247,7 +247,8 @@ export async function getActiveCompany() {
} }
/** /**
* Get all companies accessible by the current user * Get all companies accessible by the current user.
* Admins and Super Admins can access all active companies.
*/ */
export async function getUserCompanies() { export async function getUserCompanies() {
const session = await getQuestSession(); const session = await getQuestSession();
@ -256,6 +257,14 @@ export async function getUserCompanies() {
return []; return [];
} }
// Admins see all active companies
if (session.userType === 'Admin' || session.userType === 'Super Admin') {
return await db.quest_company.findMany({
where: { is_active: true },
orderBy: { display_name: 'asc' },
});
}
const questUser = await db.quest_user.findUnique({ const questUser = await db.quest_user.findUnique({
where: { id: session.questUserId }, where: { id: session.questUserId },
include: { include: {

View file

@ -14,6 +14,7 @@ export type InventorySummaryRow = {
plant_key: string; plant_key: string;
warehouse: string; warehouse: string;
on_hand_qty: number; on_hand_qty: number;
rows: number;
paint_code?: string; paint_code?: string;
cust_part_num?: string; cust_part_num?: string;
}; };
@ -50,6 +51,7 @@ function mapSummaryRow(raw: Record<string, unknown>): InventorySummaryRow {
plant_key: String(raw.PlantKey ?? ''), plant_key: String(raw.PlantKey ?? ''),
warehouse: String(raw.Warehouse ?? ''), warehouse: String(raw.Warehouse ?? ''),
on_hand_qty: Number(raw.OnHandQty ?? 0), on_hand_qty: Number(raw.OnHandQty ?? 0),
rows: Number(raw.Rows ?? 0),
paint_code: raw.PaintCode ? String(raw.PaintCode) : undefined, paint_code: raw.PaintCode ? String(raw.PaintCode) : undefined,
cust_part_num: raw.CustPartNum ? String(raw.CustPartNum) : undefined, cust_part_num: raw.CustPartNum ? String(raw.CustPartNum) : undefined,
}; };