Intelligent Inventory

Universidad Nacional de Colombia · 2026 - At present

Next.jsSupabasePostgreSQLMQTT

The Prototyping Center lends tools, components and materials out of a set of cabinets, and until now nobody could answer "who has the soldering station and since when?" without asking around. This full-stack application puts the cabinets, the inventory and the reservations in one place, records every movement, and can pop a cabinet open over MQTT from the same screen.

Architecture and Tech Stack

Core Architecture

  • Framework: Next.js 16 (App Router), server-first, with the React Compiler enabled
  • Language: TypeScript 5.9 in strict mode
  • Backend platform: Supabase — PostgreSQL, Auth and Realtime (@supabase/ssr)
  • Validation: Zod schemas as the single input gate for every server action
  • Hardware bridge: mqtt over mqtts (port 8883) against a HiveMQ-compatible broker
  • Tables and charts: TanStack Table for the admin grids, Recharts for the dashboard
  • Exports: jsPDF + jspdf-autotable for inventory reports
  • UI: shadcn/ui (Radix Nova) + Tailwind CSS 4, next-themes for light/dark

Layered Architecture

Reads and writes are separated by folder, not by convention: every mutation lives in lib/actions/* and every query in lib/data/*. A page never queries Supabase inline, and a form never reads — which is what keeps authorization checks in exactly one place per operation.

Request Flow

Key Features

Features at a Glance

Cabinets and quick open

Each cabinet is modelled digitally with its contents and state. From the admin panel a cabinet can be opened directly: the action publishes a command to the MQTT broker with its own timeout and error handling, so a broker that is down surfaces as a failed action instead of a spinner that never resolves.

Reservations

Students and staff request materials; the team approves or rejects from the same panel. The reservation carries its own lifecycle and shows up in the item's history, so a piece of equipment that is out on loan cannot be silently double-booked.

Roles and access

Authentication, profiles and role-based permissions (admin / root gates the whole /admin area). Access is checked in three layers: session refresh at the request proxy, a role gate in the admin layout, and assertAdmin() inside the actions themselves.

Access history and sessions

A full audit trail of cabinet openings and material usage, plus a session timeline view — who used what, when, and for how long.

Admin dashboard

KPIs plus five analytics tabs (overview, inventory, maintenance, operations, reservations) built on Recharts, with a data fallback so a failing analytics RPC degrades the numbers instead of taking the page down.

Maintenance

Items carry a maintenance record and history, so a tool that keeps coming back broken is visible as a pattern rather than as a series of unrelated incidents.

Technical Highlights

One cached resolver for the current user

getCurrentUser wraps the auth user + profile lookup in React's cache(), so a render tree that needs the user in the layout, the page and three components pays for exactly one lookup.

Actions return state, they don't throw

Every admin action returns a serializable AdminFormState with error, fieldErrors and success instead of throwing. Validation failures are data, not exceptions — which is what lets the same form component render field-level errors without a try/catch in the UI.

Read models composed in TypeScript

lib/data/* doesn't return raw rows: it queries several tables and composes an enriched view model (a Cabinet with its _count and item_names), so components render one object instead of stitching relations together in JSX.

Realtime as deltas, not refetches

The cabinet grid subscribes through subscribeCabinetsGrid and applies broadcast events as in-memory deltas. Two people working the same cabinet see each other's changes without either of them reloading.

One generic table for every admin module

components/tables/data-table.tsx carries the filtering, sorting and pagination once; each module (cabinets, inventory, users, maintenance, reservations, activity, sessions) only declares its typed columns.

Project Structure

app/
├── (home)/                  # cabinet grid for the whole team
├── (admin)/admin/           # inventory, cabinets, categories, maintenance,
│                            # reservations, sessions, users, activity
├── (auth)/                  # login, register, password reset + callback
├── history/  profile/       # per-user views
└── layout.tsx
components/
├── admin/                   # one folder per module (table, form, actions)
├── tables/data-table.tsx    # generic filtering/sorting/pagination
└── ui/                      # shadcn primitives
lib/
├── actions/                 # writes — one file per operation, Zod-validated
│   └── cabinets/open-with-mqtt.ts   # publishes the open command
├── data/                    # reads — one fetcher per view
├── realtime/cabinets.ts     # Supabase Realtime subscriptions
├── schemas/                 # Zod schemas
├── types/                   # enriched domain models
└── supabase/                # browser, server, proxy clients + getCurrentUser
proxy.ts                     # session refresh at the request edge

Impact and Scalability

  • Every material movement is traceable: who, what, when and for how long.
  • The MQTT bridge is in production code, so wiring a new cabinet is a hardware task, not an application change; the ESP32 / Raspberry Pi units that drive the physical locks are being rolled out.
  • The read/write split and the generic table make a new admin module mostly a matter of declaring schema, fetcher, action and columns.
  • Honest gaps: there is no automated test suite and no CI pipeline in the repository yet.

Notes

Built with Next.js 16, Supabase (PostgreSQL, Auth, Realtime), MQTT, TanStack Table, Recharts and shadcn/ui. The repository is private — it belongs to the Centro de Prototipado at the National University of Colombia.

🖼️ IMAGE PLACEHOLDER — the cabinet grid on the home screen

🖼️ IMAGE PLACEHOLDER — the admin dashboard with its KPIs and analytics tabs

🖼️ IMAGE PLACEHOLDER — the inventory table with filters and PDF export

🖼️ IMAGE PLACEHOLDER — the access history / session timeline

🖼️ IMAGE PLACEHOLDER — a physical cabinet with its ESP32 lock


© 2026 Felipe Giraldo