| AI Forecasting | Real-Time Analytics | Auto Reorder | AI Assistant |
|---|---|---|---|
| Predict demand with AI | Live inventory insights | Smart reorder points | Natural language queries |
🔗 Direct Link: StockFlow AI Demo Video
Features · Getting Started · AI Design · Troubleshooting · Screenshots
StockFlow is a full-stack inventory management SaaS built for small-to-mid e-commerce teams who've outgrown spreadsheets but don't want the cost or complexity of a traditional ERP. It combines:
| 🗃️ Core operations | Products, categories, suppliers, inventory, orders, and customers, all backed by a real relational database with correct stock-deduction/restoration logic. |
| ⚡ Real-time visibility | Low-stock and new-order alerts pushed live to every connected session via WebSockets. |
| 🤖 AI on real data | Demand forecasting, a natural-language inventory assistant, and reorder recommendations — grounded in your actual sales history, with a language model (Groq) used only for narrative insight, never for computing the numbers themselves. |
The project also includes a public marketing site (home, features, pricing, about, contact) and a fully separate authenticated dashboard.
Quick start:
cp .env.example .env && docker compose up --build— see Getting Started for the full walkthrough.
All screenshots live in
docs/screenshots/and show both light and dark modes.
- Home, Features, Pricing, About, Contact — responsive, dark-mode aware
- Dedicated "About DevHatch Labs" section on the About page
- JWT-based signup/login (email + password, bcrypt-hashed via Werkzeug)
- Role-based access control —
adminandstaffroles; public signup always createsstaff(no privilege escalation via the signup form) - Session persistence via
localStorage, automaticAuthorization: Bearerheader on every API call - Profile page showing the real logged-in user (name, email, role, member since) — reachable from the navbar's user menu, which also links to Settings and Log out
- Live stats (products, orders, low-stock count, revenue this month)
- 7-day revenue trend chart, top-5 products by units sold, recent orders, low-stock list
- Full CRUD with category and supplier assignment, price/cost tracking, low-stock threshold
- Per-product stock movement history
- Per-product AI demand forecast (see AI Features)
- Category listing; full supplier CRUD (admin-only delete, blocked if the supplier still has products)
- Stock adjustments (stock-in, stock-out, loss/damage correction), each logged as a
StockMovementwith who/when/why - AI-powered reorder recommendations panel (see AI Features)
- Order creation with automatic stock deduction across all line items (validated atomically — a failure partway through never leaves a partial order)
- Status lifecycle (
pending → processing → shipped → delivered, orcancelled), with automatic stock restoration on cancellation - Customers are auto-created/reused by email at order time
- List, create, and view aggregate stats (total orders, total spent) computed live from order history — never a stale cached counter
- Server-generated CSV exports: inventory, orders (with optional date range), revenue by month, stock movements
- Low-stock and new-order events pushed live over Socket.IO to every authenticated session, backed by Redis when available (falls back to in-process delivery in single-worker dev)
- Demand forecasting — linear-trend forecast from real order history, plus a Groq-generated narrative insight
- NL restocking assistant — a chat interface that answers questions about your live inventory, grounded in real data (never invents numbers)
- Smart reorder recommendations — demand-driven (not just threshold-based) reorder quantities, plus one Groq-generated prioritization summary across the whole list
All three AI features degrade gracefully with a clear message if no Groq API key is configured — the app never crashes or hangs waiting on the AI provider.
- Dark mode (system-aware, manual toggle, persisted)
- Fully responsive (mobile sidebar, adaptive layouts)
- Accessible forms (label association,
aria-invalid/aria-describedby,aria-labels on icon-only controls,aria-livetoasts)
| Frontend |
Next.js 14 (App Router) + React 18 + TypeScript · Tailwind CSS 3 · Recharts (charts) · Framer Motion (animation) · |
| Backend |
Flask 3 (application factory pattern, Blueprints per resource) · SQLAlchemy + Alembic (Flask-Migrate) · Flask-JWT-Extended · Marshmallow · Flask-SocketIO · PyMySQL · Gunicorn + eventlet |
| Database & Infra |
MySQL 8 (production/Docker; SQLite for isolated tests only) · Redis 7 (cache + Socket.IO message queue) · Nginx (reverse proxy) · Docker + Docker Compose |
| AI |
Groq ( |
| Testing & CI |
Pytest (backend — 42 tests) · TypeScript + ESLint (frontend) · GitHub Actions (runs both suites on every push/PR) |
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Next.js │◄────►│ Nginx │◄────►│ Flask │
│ (frontend) │ │ (reverse │ │ (backend) │
└─────────────┘ │ proxy) │ └──────┬──────┘
└──────────────┘ │
┌─────────┼─────────┐
▼ ▼ ▼
┌────────┐ ┌───────┐ ┌────────┐
│ MySQL │ │ Redis │ │ Groq │
│ (data)│ │(cache/│ │ (AI) │
│ │ │pubsub)│ │ │
└────────┘ └───────┘ └────────┘
- The frontend never talks to MySQL/Redis/Groq directly — every request goes through the Flask API.
- Redis is optional in local dev: if unreachable, the backend logs a warning and continues (Socket.IO falls back to local in-process delivery; nothing crashes).
- Groq is optional: if
GROQ_API_KEYisn't set, every AI endpoint still returns real, correctly-computed data — just with a placeholder instead of the AI-generated narrative.
Expand full tree
Stockflow_Full_Stack_Dashboard/
├── frontend/ # Next.js app (App Router)
│ ├── app/
│ │ ├── (marketing)/ # Public site: home, features, pricing, about, contact
│ │ ├── (auth)/ # Login, signup
│ │ └── dashboard/ # Authenticated app: products, orders, inventory, ai-assistant, ...
│ ├── components/ # UI components, grouped by domain (products/, orders/, ai/, ...)
│ ├── lib/ # api.ts (axios client), auth.ts, socket.ts, utils.ts
│ └── types/ # Shared TypeScript types
│
├── backend/ # Flask app
│ ├── app/
│ │ ├── models/ # SQLAlchemy models
│ │ ├── routes/ # Blueprints (one file per resource)
│ │ ├── schemas/ # Marshmallow request validation
│ │ ├── services/ # Alert/report business logic
│ │ ├── ai/ # forecasting.py, assistant.py, restock_recommender.py
│ │ ├── sockets/ # Socket.IO event handlers
│ │ └── utils/ # RBAC decorator, shared error helpers
│ ├── migrations/ # Alembic migrations
│ ├── tests/ # Pytest suite (42 tests)
│ └── seed.py # Seeds sample data for local dev
│
├── nginx/nginx.conf # Reverse proxy config
├── docker-compose.yml # Full local stack (mysql, redis, backend, frontend, nginx)
├── .github/workflows/ci.yml # GitHub Actions CI
└── .env.example # Every environment variable the stack needs
Requires Docker Desktop.
cp .env.example .env
# edit .env if you want to change default passwords or add GROQ_API_KEY
docker compose up --buildThis starts MySQL, Redis, the Flask backend, the Next.js frontend, and Nginx together. Once healthy, the app is available at http://localhost (routed through Nginx).
Note: the
mysqlcontainer publishes to host port 3307, not 3306 — this avoids colliding with a MySQL install already running on your machine (a common conflict on Windows/Mac dev boxes). Nothing else needs to change: the backend talks to MySQL over the internal Docker network (mysql:3306), never through the host port. Port 3307 only matters if you want to connect an external MySQL client (Workbench, DBeaver) to the containerized database.
Seed sample data (run once, in a separate terminal, while the stack is up):
docker compose exec backend python seed.pyPrerequisites: Node.js 20+, Python 3.11+, a running MySQL 8 instance, (optionally) Redis.
Backend
cd backend
python -m venv .venv
source .venv/Scripts/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
# Set env vars (see .env.example) or export them directly:
export DATABASE_URL="mysql+pymysql://user:pass@localhost:3306/stockflow"
export JWT_SECRET_KEY="a-real-secret-at-least-32-bytes-long"
export SECRET_KEY="another-real-secret"
export FLASK_ENV=development
flask db upgrade # apply migrations
python seed.py # optional: sample data
python main.py # runs on http://localhost:5000Frontend
cd frontend
npm install
npm run dev # runs on http://localhost:3000Seeded login (from seed.py):
| Role | Password | |
|---|---|---|
| Admin | admin@stockflow.dev |
password123 |
| Staff | staff@stockflow.dev |
password123 |
See .env.example for the full, authoritative list. Summary:
| Variable | Used by | Notes |
|---|---|---|
MYSQL_ROOT_PASSWORD, MYSQL_DATABASE, MYSQL_USER, MYSQL_PASSWORD |
Docker Compose | MySQL container credentials |
DATABASE_URL |
Backend | SQLAlchemy connection string |
SECRET_KEY, JWT_SECRET_KEY |
Backend | Use long, random values in production |
REDIS_URL |
Backend | Optional — app degrades gracefully if unreachable |
CORS_ORIGINS |
Backend | Comma-separated list of allowed frontend origins |
GROQ_API_KEY |
Backend | Optional — get a free key at console.groq.com/keys; AI features degrade gracefully without it |
NEXT_PUBLIC_API_URL |
Frontend | Backend API base URL |
NEXT_PUBLIC_SOCKET_URL |
Frontend | Backend Socket.IO URL |
Schema is managed with Alembic (backend/migrations/). To apply migrations:
cd backend
flask db upgradeTo create a new migration after changing a model:
flask db migrate -m "describe the change"
flask db upgradepython seed.py drops and recreates all tables, then inserts: 2 users (admin + staff), 5 categories, 3 suppliers, 8 products, 5 customers, and 5 orders with line items — enough to exercise every feature (including low-stock items and a cancelled order) out of the box.
All endpoints are prefixed /api and (except auth) require Authorization: Bearer <token>.
| Resource | Endpoints |
|---|---|
| Auth | POST /auth/signup, POST /auth/login, GET /auth/me |
| Products | GET/POST /products, GET/PUT/DELETE /products/:id, GET /products/:id/movements, GET /products/:id/forecast |
| Categories | GET/POST /categories |
| Suppliers | GET/POST /suppliers, PUT/DELETE /suppliers/:id |
| Inventory | POST /inventory/adjust, GET /inventory/recommendations |
| Orders | GET/POST /orders, GET /orders/:id, PUT /orders/:id/status |
| Customers | GET/POST /customers, GET /customers/:id |
| Dashboard | GET /dashboard/{stats,revenue,top-products,recent-orders,low-stock} |
| Reports | GET /reports/{inventory,orders,revenue,movements} (CSV) |
| AI Assistant | POST /ai/chat |
| Health | GET /health |
Admin-only endpoints (product/supplier delete) return 403 for non-admin tokens. See backend/tests/ for the exact contract of every endpoint, verified by the test suite.
The backend emits two Socket.IO events to all authenticated, connected clients:
low_stock_alert— fires when a product's stock crosses at-or-below its threshold (not on every subsequent decrease, to avoid spam)new_order_alert— fires on every new order
The Socket.IO handshake requires a valid JWT (auth: { token }); unauthenticated connections are rejected. The frontend shows these as toast notifications app-wide via DashboardLayout.
All three AI features follow the same principle: deterministic calculation for any number that matters, Groq only for narrative/prioritization — a language model is never asked to compute a forecast or a quantity, which avoids hallucinated figures.
| Feature | Numbers computed by | Groq's role |
|---|---|---|
| Demand forecasting | Least-squares linear regression over 8 weeks of real order history | 2–3 sentence insight on the trend + a recommendation |
| NL assistant | — | Answers grounded in a live snapshot of your catalog, low-stock items, and top sellers; instructed to never invent numbers |
| Reorder recommendations | avg_weekly_sales × coverage_weeks − current_stock, only surfaced when real sales velocity indicates a stockout risk |
One summary call prioritizing across the whole list (not one call per product) |
Get a free Groq API key at console.groq.com/keys (no credit card required) and set GROQ_API_KEY to enable the narrative layer. Every feature works correctly (real numbers, no crashes) even without a key — you'll just see a placeholder instead of the AI-generated text. Model used: llama-3.3-70b-versatile.
Backend (42 tests covering auth, RBAC, CRUD, stock deduction/restoration, forecasting math, AI graceful-degradation):
cd backend
pip install -r requirements-dev.txt
pytest -vFrontend:
cd frontend
npx tsc --noEmit # type-check
npm run lint # ESLint
npm run build # production build.github/workflows/ci.yml runs on every push/PR to main and feat/**/fix/** branches:
- backend job: installs deps, runs the full pytest suite
- frontend job: installs deps, type-checks, lints, and runs a production build
Dev-mode issues actually hit while building this project, and how to recognize/fix them — click to expand
Page fails with "The default export is not a React Component in page: /some-route"
An app/**/layout.tsx or page.tsx file has no content (0 bytes) — Next.js requires every file it finds in the app/ router to export a valid component. This can happen from an accidental save/edit that wipes a file, or from git operations (see below). Check the file's actual size/content; if it's empty, restore it from git history:
git log --oneline -- path/to/file.tsx
git show <commit-before-it-broke>:frontend/path/to/file.tsxBuild error pointing at a file inside node_modules (e.g. "Module not found: Can't resolve '../util/X'")
A corrupted/incomplete package install — a folder exists but is missing files it should have. Reinstall just that package rather than wiping all of node_modules:
cd frontend
rm -rf node_modules/<package-name>
npm install <package-name>Frontend shows stale/wrong content, or errors referencing files that were already deleted from the codebase
Stale .next build cache — common after switching branches, git reset --hard, or any operation that changes many files while the dev server is running. Stop the dev server, then:
cd frontend
rm -rf .next node_modules/.cache
npm run devAI features show a "not configured" placeholder despite adding a Groq key
See AI Features and Environment Variables — most likely the .env file is in the wrong location for how you're running the app (backend/.env for manual runs vs. repo-root .env for Docker Compose), or the pasted key is the wrong provider's format (Groq keys start with gsk_, not sk-... or AIzaSy...).
Login/API calls intermittently fail with a connection error, even though the backend "is running"
Symptom: requests to /api/... randomly fail (ERR_CONNECTION_REFUSED) while the backend log shows repeated Restarting with watchdog / Detected change in '...site-packages...' lines. This is Flask's debug auto-reloader watching every installed package on disk and false-triggering a restart, which briefly drops the port mid-request. backend/main.py now runs with use_reloader=False, so this shouldn't recur — if you see it again, confirm that line wasn't reverted.
docker compose up fails on the mysql service: "ports are not available ... bind: Only one usage of each socket address"
Something else on your machine (often a local MySQL install/service) already has port 3306. This project's docker-compose.yml publishes MySQL on host port 3307 specifically to avoid this — if you're still hitting it, you likely have an old container or another service bound to the port in question; check with netstat -ano | findstr :3306 (Windows) or lsof -i :3306 (Mac/Linux) and stop whatever owns it, or change the host-side port in docker-compose.yml to something free.
Socket.IO fails with RuntimeError: Redis requires a monkey patched socket library to work with eventlet, and dashboard API calls start timing out (504) right after
Only happens when Redis is actually reachable (i.e. in Docker — local dev without Redis never hits this path). flask-socketio uses Redis as its cross-worker message queue when available, which requires eventlet.monkey_patch() to run before anything else imports socket. backend/main.py calls this as its very first line; if you see this error, something is importing a module before that patch runs (check nothing was added above it).
Dashboard/API calls all return 401 Unauthorized after the backend has been restarted or rebuilt several times
Your browser's localStorage is holding a token from an earlier session that no longer matches the currently-running backend. Open DevTools → Application → Local Storage → delete stockflow_token and stockflow_user, then log in again for a fresh token.
Intended production targets: Vercel (frontend), Render (backend), a hosted MySQL provider, and Redis Cloud — all of which have free tiers suitable for this project. Deployment is tracked as a separate phase; see Build Roadmap below.
StockFlow was built in phases, each independently tested end-to-end (backend: pytest + live curl verification; frontend: type-check, lint, production build, and browser verification) before moving to the next:
- Phase 1 — Frontend UI against mock data (Next.js, all pages, dark mode, marketing site)
- Phase 2 — Backend (Flask scaffold, DB models/migrations, JWT auth + RBAC, core CRUD, real-time alerts, CSV reports, frontend integration, Nginx + CI)
- Phase 3 — AI (demand forecasting, NL restocking assistant, smart reorder recommendations)
- Phase 4 — Live deployment (Vercel + Render + hosted MySQL + Redis Cloud)
Build Smarter. Scale Faster.
DevHatch Labs' vision is to become a leading AI solutions company, helping businesses adopt artificial intelligence through practical, scalable, and affordable implementations. Our mission is centered on building practical AI solutions that create measurable business impact.
|
AI Services
|
Web & Growth
|
© DevHatch Labs. All rights reserved.











































