DEV Community

Roberto Luna
Roberto Luna

Posted on

Enriching PC Inventory with Domain, Installation Year, and Asset Tag from a PDF Report

Enriching PC Inventory with Domain, Installation Year, and Asset Tag from a PDF Report

TL;DR: I added three new fields (domain, yearInstall, assetTag) to the PC inventory schema, propagated them through the TypeScript types, and displayed them in the inventory table. This lets the UI show data extracted from the legacy Computers_By_Age.pdf report without breaking existing queries.


The Problem

Our internal inventory UI (pcview) shows a list of workstations pulled from a Prisma‑managed PostgreSQL database. The UI was missing three pieces of information that the IT department still tracks in a legacy PDF report called Computers_By_Age.pdf:

  1. Domain – the AD domain the PC belongs to.
  2. Installation Year – the year the OS was first installed.
  3. Asset Tag – the physical tag printed on the machine.

The PDF is manually updated weekly, and we import its data into the database via a one‑off script. Until now the PcDevice model only had generic fields like hostname, os, and lifecycleStatus. When the UI tried to render the new columns, TypeScript threw errors because the PcDeviceRow interface didn’t contain those properties, and the Prisma client failed at compile‑time because the schema didn’t have the columns.

The symptom in the console was:

Property 'domain' does not exist on type 'PcDeviceRow'.
Error: Cannot query field "domain" on type "PcDevice".
Enter fullscreen mode Exit fullscreen mode

What I Tried First

My initial attempt was to add the columns only on the frontend. I edited src/features/inventory/InventoryTable.tsx to reference info.getValue() for domain, yearInstall, and assetTag, assuming the Prisma client would ignore unknown fields. Unsurprisingly, the TypeScript compiler complained about missing properties in PcDeviceRow, and the backend threw a GraphQL error because the Prisma query tried to select non‑existent columns.

I also tried a quick raw SQL migration (ALTER TABLE PcDevice ADD COLUMN domain TEXT;) directly on the DB, but that left the Prisma schema out of sync, causing prisma generate to fail with:

P1012: The table "PcDevice" does not exist in the database.
Enter fullscreen mode Exit fullscreen mode

Both approaches failed because the schema, the type definitions, and the UI need to stay in lockstep.


The Implementation

The fix required three coordinated changes:

  1. Extend the Prisma schema – add the three fields to PcDevice.
  2. Regenerate the Prisma client – so TypeScript gets the new types.
  3. Update the TypeScript interface used by the store hook.
  4. Add the new columns to the inventory table – map the fields to UI cells.

Below are the exact diffs and the rationale behind each change.

1. Prisma schema (prisma/schema.prisma)

@@ -38,6 +38,15 @@ model PcDevice {
   lifecycleStatus String?
   lifecycleNote   String?

+  // Enriquecimiento manual desde Computers_By_Age.pdf (reporte Zoho,
+  // corporativo-wide filtrado a sol)
+  domain        String?   @db.VarChar(255) // AD domain name
+  yearInstall   String?   @db.VarChar(4)   // Year of OS installation (e.g., "2022")
+  assetTag      String?   @db.VarChar(50)  // Physical asset tag
+
   // Timestamps
   createdAt     DateTime  @default(now())
   updatedAt     DateTime  @updatedAt
Enter fullscreen mode Exit fullscreen mode

Why?

  • String? makes each column optional because the PDF may not have a value for every machine.
  • Adding @db.VarChar keeps the underlying column size reasonable.
  • The comment block documents the source of the data (the PDF) for future maintainers.

After committing the schema change, I ran:

npx prisma migrate dev --name enrich-pc-fields
npx prisma generate
Enter fullscreen mode Exit fullscreen mode

This created a migration file that adds the three columns with NULL defaults and regenerated the client so prisma.pcDevice now knows about domain, yearInstall, and assetTag.

2. Store hook interface (src/hooks/use-devices-store.ts)

@@ -17,6 +17,11 @@ export interface PcDeviceRow {
   isOnline: boolean;
   lifecycleStatus: string | null;
   lifecycleNote: string | null;
+  domain: string | null;
+  yearInstall: string | null;
+  assetTag: string | null;
Enter fullscreen mode Exit fullscreen mode

The PcDeviceRow type mirrors the Prisma model used in the UI. Adding the three fields prevents the “Property does not exist” TypeScript error when we reference them in the table component.

3. Inventory table columns (src/features/inventory/InventoryTable.tsx)

@@ -54,6 +54,23 @@ const columns: ColumnDef<PcDeviceRow>[] = [
   header: "OS",
   cell: (info) => <span className="text-xs">{(info.getValue() as string) ?? "—"}</span>,
 },
+{
+  accessorKey: "domain",
+  header: "Domain",
+  cell: (info) => (
+    <span className="text-xs font-mono">{(info.getValue() as string) ?? "—"}</span>
+  ),
+},
+{
+  accessorKey: "yearInstall",
+  header: "Install Year",
+  cell: (info) => (
+    <span className="text-xs">{(info.getValue() as string) ?? "—"}</span>
+  ),
+},
+{
+  accessorKey: "assetTag",
+  header: "Asset Tag",
+  cell: (info) => (
+    <span className="text-xs font-mono">{(info.getValue() as string) ?? "—"}</span>
+  ),
+},
Enter fullscreen mode Exit fullscreen mode

Why these settings?

  • accessorKey tells react-table which property of PcDeviceRow to pull.
  • The cell renderers use a fallback "—" for null values to keep the UI tidy.
  • I added font-mono for domain and assetTag because they are identifier strings.

4. Data import script (not part of the diff but worth mentioning)

The weekly script that parses Computers_By_Age.pdf now does:

await prisma.pcDevice.upsert({
  where: { hostname },
  update: {
    domain,
    yearInstall,
    assetTag,
  },
  create: {
    hostname,
    os,
    // other required fields...
    domain,
    yearInstall,
    assetTag,
  },
});
Enter fullscreen mode Exit fullscreen mode

Because the Prisma model now contains the new fields, the script runs without modification beyond adding the three variables.


Key Takeaway

When extending a data model that spans database → Prisma client → TypeScript → UI, you must update every layer in lockstep. Adding a column only in the DB or only in the UI will break the type chain and surface as compile‑time or runtime errors. A single coordinated PR that touches schema, generated types, and UI components is the safest way to keep the stack consistent.


What's Next

The next step is to automate the PDF ingestion with a CI job that runs the parsing script nightly and pushes the changes via a Prisma migration. I’ll also add column-level sorting for the new fields in InventoryTable.tsx by extending the ColumnDef with enableSorting: true and tweaking the server‑side query to support ordering on domain, yearInstall, and assetTag.


Roberto Luna Osorio – Full Stack Developer & Project Lead

Playa del Carmen, México


Tags: #vibecoding #buildinpublic #typescript #react #prisma #nextjs #postgresql


Part of my Build in Public series — sharing the real process of building SaaS projects from Playa del Carmen, México.

Repo: zaerohell/pcview · 2026-09-08

#playadev #buildinpublic

Top comments (0)