A focused, responsive web workspace for medication stock tracking, batch numbers, expiration monitoring, and unit movements.
Overview · Showcase · Features · Architecture · Storage & Limits · Tech Stack · Quick Start
Medication Inventory is a client-side pharmaceutical and clinical stock management application designed with a clean teal-green identity, light/dark theme adaptation, and responsive desktop/mobile layouts.
It gives clinic staff, pharmacy supervisors, and small healthcare teams instant visibility into stock levels, batch numbers, manufacturers, and expiration schedules without requiring complex server infrastructure or third-party accounts.
Watch the short product walk-through demonstrating initial stock review, adding records, search, status filtering, stock adjustments, and theme switching:
The animated preview shows a short excerpt of the canonical showcase. Open the full video below for the complete flow.
- Inventory overview — Real-time classification into Healthy, Low Stock, Expiring Soon (30-day window), and Expired.
- Interactive dashboard — Click any metric card to filter the inventory list to matching batches.
- Status filters — Narrow the inventory to needs attention, healthy, low stock, expiring soon, or expired records without duplicating the dashboard summary.
- Batches & expiration control — Strict date validation preventing invalid calendar dates and retaining proper records.
- Safe stock adjustments — Add or remove units with a live post-adjustment balance preview and bounds checks preventing negative stock.
- Data portability — Export inventory to spreadsheet-safe CSV, download versioned JSON backups, and restore validated backups only after explicit confirmation.
- Faceted search & sorting — Search by medication name, active ingredient, batch number, or manufacturer, with sorting by Name, Expiration, or Quantity.
- Light & dark themes — Built-in theme switcher with flash-free initial hydration and system color scheme detection.
- Accessible & compliant — Full keyboard trap in modals, ARIA labels, live status regions, and
prefers-reduced-motionrespect.
The codebase follows a straightforward, low-friction architecture without unnecessary abstraction layers:
src/
├── components/
│ ├── domain/ # Core domain UI (DashboardStats, InventoryTable, MedicationForm)
│ └── ui/ # Reusable primitives (Button, Card, Input, Modal)
├── hooks/
│ ├── useInventory.ts # Inventory state management and safe persistence boundary
│ └── useTheme.ts # Light/Dark mode state and DOM synchronization
├── lib/
│ ├── dataPortability.ts # CSV export and versioned JSON backup/restore validation
│ ├── inventory.ts # Filtering and search predicates
│ ├── medications.ts # Domain logic (status calculation, stock math, date parsers)
│ ├── metrics.ts # Dashboard statistics calculation
│ ├── storage.ts # Malformed record sanitization and localStorage I/O
│ └── utils.ts # Style merging utility (clsx + tailwind-merge)
└── types/
└── index.ts # Core TypeScript types (Medication, MedicationStatus, etc.)
- Storage mechanism: Persistent local storage under key
medication-inventory-data. - Sanitization:
parseStoredMedicationsverifies every field on startup, rejecting corrupted records, invalid types, and impossible calendar dates. - Backup validation: JSON restore is versioned and atomic; the full backup is rejected when a record is invalid, medication IDs are duplicated, or stock values exceed the supported safe-integer boundary.
- Atomic persistence & Error handling: State mutations are atomic—in-memory inventory is only committed if localStorage persistence succeeds. If browser storage fails (e.g.
QuotaExceededErroror private browsing restrictions), operations display proportional alert notices, preserve open dialogs and entered data for retry, and never falsely claim changes were saved. - Representation limit: Stock adjustments enforce JavaScript's safe integer boundary (
Number.MAX_SAFE_INTEGER, 9,007,199,254,740,991 units) to prevent numeric overflow while strictly preserving user data.
| Area | Technologies |
|---|---|
| Frontend | React 19, TypeScript 5.9 |
| Styling | Tailwind CSS 4.3, Custom CSS Tokens (Teal theme) |
| Icons | Lucide React |
| Tooling | Vite 7 |
| Testing | Vitest 4, Playwright |
| Linting | ESLint 10 |
| Container | Docker node:22-alpine + nginx:1.30.4-alpine (optional preview/packaging) |
| CI | GitHub Actions |
- Node.js 22 (recommended for Vite 7) and npm (use
npm cito respectpackage-lock.json) - Optional for showcase capture and QA: Chrome/Playwright, FFmpeg, and an active local preview/dev server
git clone https://github.com/LeoneMarcos/medication-inventory.git
cd medication-inventorynpm cinpm run devOpen the local server URL printed by Vite (typically http://localhost:5173).
As an optional reproducible local preview and container packaging path (canonical production deployment remains Cloudflare static delivery), you can run the application in an isolated, production-oriented Nginx (nginx:1.30.4-alpine) container with baseline security headers:
# Using Docker Compose (Recommended)
docker compose up -d
# Or build and run directly with Docker
docker build -t medication-inventory .
docker run -d -p 8081:80 --name medication-inventory medication-inventoryAccess the application in your browser at http://localhost:8081.
To stop the container:
docker compose downRun the automated test suite, type-checking, and lint checks:
# Run unit and boundary tests
npm test
# Check types
npm run typecheck
# Run ESLint
npm run lint
# Check formatting
npm run format:check
# Install the Chromium browser required by Playwright (once per environment)
npx playwright install chromium
# Run Playwright end-to-end tests
npm run test:e2e
# Build production bundle
npm run buildThe automated CI pipeline enforces quality checks on Node.js 22.x, covering security audits, formatting, linting, TypeScript compilation (tsc -b), Vitest unit tests, production build validation, and Playwright Chromium E2E testing. The Publish Showcase GitHub Actions workflow regenerates the canonical video, hero screenshot, and short README GIF preview when product/showcase inputs change, and can also be run manually; those stable media paths are reused by the project README, profile, and portfolio.
node scripts/negative-storage-qa.mjs— Automated verification of storage failure modals and retry flow. Requires an active local application server (defaulthttp://127.0.0.1:4181/or configured viaBASE_URL).
ARCHITECTURE.md— System architecture and component contracts.DESIGN.md— Visual tokens, layout specifications, and interaction states.PRODUCT.md— Product definition and requirements.STACK.md— Technology stack constraints and rules.TEST_PLAN.md— Comprehensive verification strategy.docs/STATUS.md— Status log and release evidence.
Licensed under the Apache License 2.0. See LICENSE for details.

