Developer Docs

Inventory Health & Deprecation Tracking

Two new admin dashboard tabs โ€” Inventory and Deprecation โ€” provide operational visibility into product stock health and API lifecycle management.

NOTE

Both panels are lazy-loaded and only accessible to users with the PLATFORM_ADMIN role. See Custom RBAC for permission details.

Architecture Overview

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Admin Dashboard  (/admin)                               โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”        โ”‚
โ”‚  โ”‚Overview โ”‚ โ”‚Users  โ”‚ โ”‚Analytics โ”‚ โ”‚Payments โ”‚  More โ–พ โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚      โ”‚โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”              โ”Œโ”€โ”€โ”ดโ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚Inventory โ”‚ โ”‚Deprecation     โ”‚              โ”‚Trash โ”‚  โ”‚
โ”‚  โ”‚  Panel   โ”‚ โ”‚  Tracker       โ”‚              โ”‚Hist. โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜              โ”‚Temp. โ”‚  โ”‚
โ”‚       โ”‚               โ”‚                       โ”‚Searchโ”‚  โ”‚
โ”‚       โ–ผ               โ–ผ                       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚  GET_ADMIN_PRODUCTS   GET /admin/deprecated-fields      โ”‚
โ”‚  (GraphQL)            (REST)                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Inventory Health Panel

Data Source

The Inventory panel consumes the existing GET_ADMIN_PRODUCTS GraphQL query, which returns product data including totalInventory and the associated Malet handle. All stock analysis is performed client-side โ€” no additional backend APIs are required.

Store: `inventoryStore.svelte.ts`

Pure functions exported for testing and reuse:

// Core analysis
getStockStatus(totalInventory: number, threshold: number): StockStatus
enrichWithStockInfo(products: AdminProduct[], threshold: number): ProductStockInfo[]
computeLowStockProducts(products: AdminProduct[], threshold: number): ProductStockInfo[]
computeOutOfStockProducts(products: AdminProduct[]): ProductStockInfo[]
computeHealthSummary(products: AdminProduct[], threshold: number): InventoryHealthSummary

// Configuration
validateThreshold(value: number): { valid: boolean; error?: string }
loadThreshold(): number       // from localStorage
saveThreshold(value: number)  // to localStorage

Types

type StockStatus = 'ok' | 'low' | 'out';

interface ProductStockInfo {
  id: string;
  name: string;
  totalInventory: number;
  trackInventory: boolean;
  status: StockStatus;
  delta: number;           // threshold - inventory (negative = above)
  maletHandle?: string;
}

interface InventoryHealthSummary {
  total: number;           // all products
  tracked: number;         // products with inventory tracking
  ok: number;
  low: number;
  out: number;
}

Threshold Configuration

Property Value
Default 10 units
Range 1 โ€“ 100
Validation Zod z.number().int().min(1).max(100)
Persistence localStorage key: ngwenya_low_stock_threshold

IMPORTANT

The threshold is a client-side setting, not stored on the backend. A future enhancement could add a lowStockThreshold field to the Product entity for per-product or per-Malet configuration.

Component: `InventoryPanel.svelte`

Props:

{ products: AdminProduct[] }  // passed from admin +page.svelte

Sections:

  1. Health Summary Cards โ€” clickable filters (All / OK / Low / Out)
  2. Stock Table โ€” product name, Malet handle, inventory count, threshold delta, status badge
  3. Config Sidebar โ€” threshold slider + number input, summary stats

Deprecation Tracking Panel

Data Source

Unlike other admin panels that use GraphQL, the Deprecation Tracker consumes the gateway's REST API directly:

Endpoint Method Description
/admin/deprecated-fields GET Returns the full usage report
/admin/deprecated-fields/reset POST Resets all in-memory counters

These endpoints are served by the DeprecationController in the gateway's DeprecationModule, backed by the FieldUsageService which uses the FieldUsagePlugin (Apollo Server plugin) to intercept and record deprecated field access during GraphQL resolution.

WARNING

Usage counters are in-memory and reset on gateway restart. They are not persisted to a database. This is intentional for simplicity โ€” a Redis-backed persistence layer can be added if needed.

Store: `deprecationStore.svelte.ts`

// API
fetchDeprecationReport(): Promise<DeprecationReport>
resetDeprecationCounters(): Promise<{ message: string }>

// Pure functions
sortByUsageCount(records: FieldUsageRecord[]): FieldUsageRecord[]
sortByLastSeen(records: FieldUsageRecord[]): FieldUsageRecord[]
groupByType(records: FieldUsageRecord[]): Record<string, FieldUsageRecord[]>
computeSummary(report: DeprecationReport): SummaryStats
formatFieldPath(record: FieldUsageRecord): string  // "Type.field"
formatTimestamp(dateStr: string | null): string

Types

interface FieldUsageRecord {
  typeName: string;
  fieldName: string;
  deprecationReason: string;
  usageCount: number;
  firstSeenAt: string | null;
  lastSeenAt: string | null;
}

interface DeprecationReport {
  fields: FieldUsageRecord[];
  totalTracked: number;
  totalUsage: number;
}

Component: `DeprecationTracker.svelte`

Sections:

  1. Summary Bar โ€” total tracked fields, total usage count, most-used field
  2. Sort Toggle โ€” switch between "By Usage" and "By Recent"
  3. Fields Table โ€” Type.field path, deprecation reason, usage count (color-coded: red > 100, amber > 10), timestamps
  4. Reset Counters โ€” button with confirmation dialog

Admin Dashboard Navigation

With 10 total tabs, the admin dashboard uses a primary + overflow pattern:

Primary Tabs (always visible) More โ–พ Dropdown
Overview, Users, Analytics, Payments, Inventory Trash, Edit History, Templates, Search Config, Deprecation

When a "More" tab is active, the dropdown button shows the active tab's label and highlights with the accent color.

Testing

Test File Tests Coverage
tests/inventoryStore.test.ts 21 Stock status, enrichment, filtering, summary, threshold validation, constants
tests/deprecationStore.test.ts 11 Sorting, grouping, summary computation, formatting

Run:

npx vitest run tests/inventoryStore.test.ts tests/deprecationStore.test.ts