eStock – Inventory Control System
2025Real-time inventory management system built for small businesses. Firebase-backed product catalog with brand/model/name taxonomy, quantity tracking, bulk operations, CSV import, and a formatted list export feature — all behind Firebase Auth with Redux-persisted session state.
eStock is a browser-based inventory management system designed for small businesses that need a reliable alternative to spreadsheets and WhatsApp lists. It stores products in Firestore with a three-level taxonomy (brand → model → name), tracks quantities in real time across sessions, and lets teams export formatted inventory lists with a single click. The entire app is protected by Firebase Auth with session persistence, meaning a page refresh doesn't require re-login.
Small retail and distribution businesses in Brazil often track inventory through shared spreadsheets or informal WhatsApp group messages. These systems break down quickly: concurrent edits cause data loss, there's no history of quantity changes, and generating a formatted product list for a supplier or client requires manual copy-paste work. eStock was built to replace that workflow with a structured, real-time web app.
Build a multi-user inventory system that: (1) organizes products by brand, model, and name for fast filtering; (2) supports quantity increments and decrements with validation (no negative stock); (3) allows bulk CSV import for initial data migration; (4) generates formatted text lists ready to paste into messages or emails; (5) requires no backend infrastructure beyond Firebase.
- ◈Solo developer — architecture, frontend, data modeling, Firebase integration
- 01Firebase Firestore as the primary database — Firestore's real-time subscription model means any quantity update is reflected instantly across all open sessions without manual polling. Its document-collection structure maps cleanly to the brand/model/name taxonomy, and Firebase Auth + Firestore security rules enforce row-level access without a dedicated backend.
- 02Redux Toolkit + Redux Persist for global state — rather than using Context API or local state for the product list, Redux Toolkit provides predictable, serializable state updates with async thunks for Firestore operations. Redux Persist serializes the store to localStorage so the product list survives page refreshes — important for slow connections where a Firestore fetch on every load would degrade UX.
- 03Optimistic UI for quantity updates — the updateProductQuantity thunk dispatches the new quantity to Redux immediately, then writes to Firestore. If the write fails, a compensating dispatch reverts the local state to the previous value. This makes quantity increments feel instant even on slow connections.
- 04Dual UI libraries: Ant Design + Material UI — Ant Design's Table component was chosen for StockManagement because of its built-in multi-column sort, filter, and pagination. Material UI was used for other layout and form components where its design system aligned better. Mixing libraries adds bundle weight, but the productivity gains justified it for a business tool.
- 05Formik + Yup for form validation — the AddProduct form has non-trivial validation: quantity must be a positive number on first creation, brand and model can be either selected from existing values or typed as new ones. Yup schemas express these rules declaratively and integrate cleanly with Formik's field-level error display.
- 06PapaParse CSV import with ref-forwarded file input — CSV bulk import uses PapaParse to parse browser-selected files into product objects, then writes each to Firestore. The file input element is hidden and triggered programmatically via forwardRef, keeping the UI clean — a visible button in the toolbar triggers the hidden input without exposing raw file input styling.
- 07Three-level product taxonomy (brand → model → name) — instead of a single product name field, products are organized by brand (marca), model (modelo), and specific name (nome). This structure enables the list export feature to filter and group at any level, and prevents ambiguous duplicates — the uniqueness check queries all three fields together before allowing a new product.
- 08Formatted list export for real-world shareability — the ListExport page converts filtered products into a plain-text string (optionally including quantities) and copies it to the clipboard. Business owners needed to paste inventory lists into WhatsApp messages, supplier emails, and order forms. The toggle for including or excluding quantities handles both use cases.
- 09AuthObserver + session timeout hook — an AuthObserver component subscribes to Firebase's onAuthStateChanged at the app root, syncing auth state to Redux without prop drilling. A separate useSessionTimeout hook automatically signs out the user after a configurable period of inactivity, which is important for shared devices in a warehouse or retail environment.
- →Optimistic updates significantly improve perceived performance for high-frequency actions like stock adjustments — but require careful rollback logic to keep UI and database in sync when writes fail.
- →Firebase Firestore security rules are a first-class architecture concern: defining them upfront alongside the data model prevented having to refactor access control mid-project.
- →Mixing Ant Design and MUI in the same project is manageable but requires intentional scoping — both libraries inject global CSS resets and their theming systems don't compose natively.
- →PapaParse's configuration surface is wide enough to handle messy real-world CSVs (inconsistent headers, extra whitespace), but validating and mapping parsed rows to your data model still requires explicit transformation logic.
- →Redux Persist solves the page-refresh UX problem elegantly, but serializing large product lists to localStorage can cause hydration latency on initial load — worth profiling before enabling persistReducer on large datasets.
Functional — deployed and used in production by a small business client