bikefy
Platform for bicycle workshop management that tracks inventory, repairs, and finances in one place, replacing spreadsheets and cutting manual errors.
About the project
Bicycle workshops handle multiple fronts at once: parts, repairs, finances, and suppliers. bikefy centralizes that entire operation on a single platform, eliminating spreadsheets and paperwork.
The system records every movement—from buying a part from a supplier to selling it to a customer—keeping full traceability and updating stock automatically.
Objectives:
- Inventory control with stock traceability
- Purchase and sale recording with automatic stock updates
- Repair and maintenance service management
- Financial management with balances, income, and expenses
- Kanban board for team tasks
- User management with roles (admin and employee)
Key features
| Module | Features |
|---|---|
| Inventory | Item CRUD, automatic stock, categories, purchase/sale prices, Excel/PDF export |
| Purchases & Sales | Transaction recording linked to suppliers, automatic stock adjustment, complete traceability |
| Finances | General balance, income/expense charts, transaction history |
| Services | Repair recording, pricing, payment methods, service status |
| Suppliers | CRUD with contact details, associated purchase history |
| Tasks | Kanban board with draggable columns and cards |
| Users | Admin/employee roles, JWT authentication, email password recovery |
Tech stack
| Layer | Technology | Purpose |
|---|---|---|
| Frontend | Next.js 15 (Pages Router) | React framework with SSR |
| Tailwind CSS + CSS Variables | Utility-first styles with light/dark theme | |
| Shadcn/ui (New York) + Radix UI + MUI | Accessible UI components | |
| Formik + Yup | Form handling and validation | |
| Chart.js + react-chartjs-2 | Finance charts | |
| Axios | HTTP client for API consumption | |
| Backend | Express.js 4 | REST API |
| express-validator + Joi | Input data validation | |
| jsonwebtoken | JWT Bearer authentication | |
| bcrypt | Password hashing | |
| Cloudinary | Profile image storage | |
| Nodemailer | Email sending (password recovery) | |
| Database | PostgreSQL 16 | Relational database |
| pg (Client) | Connection driver | |
| Deploy | Vercel (backend) + PM2 (frontend) | Production |
Purchase flow
User ──▶ Form (Frontend)
│
▼
POST /api/inventory/purchase
│
▼
inventory.controller → inventory.service
│
├──▶ 1. Creates transaction (INSERT transaction)
├──▶ 2. For each item:
│ ├──▶ Does the item exist?
│ │ ├── Yes → updateStock(id, quantity, 'add')
│ │ └── No → createItem(new item)
│ ├──▶ Creates detail (INSERT transaction_item)
│ └──▶ Links supplier (INSERT/UPDATE item_supplier)
│
▼
Stock updated + transaction recorded
Sale flow
User ──▶ Selects items from inventory
│
▼
validateStock() — Is there enough stock?
│
├── No → Error: "Insufficient stock"
│
└── Yes → Creates 'sale' transaction
│
└──▶ For each item:
├── updateStock(id, quantity, 'subtract')
└── Creates detail (INSERT transaction_item)
Deletion with rollback
When deleting a purchase or sale, the system automatically reverts the stock:
- Delete sale → returns stock to inventory (
add) - Delete purchase → deducts the purchased stock (
subtract), and if the supplier has no remaining active transactions with that item, it is unlinked
My role and contribution
Within the 4-person team, I was responsible for the complete design and implementation of the following modules, covering both backend and frontend:
Inventory
Full CRUD of items with name, description, category, stock, and sale price. Each item can be linked to one or more suppliers with their respective purchase price and date.
Frontend components:
InventoryTable— table with search, filters by category and supplier, sortingAddItemDialog/EditItemDialog— forms with Yup validationItemDetailsDialog— detailed view with associated suppliersExportButtons— export to Excel (XLSX) and PDF (jsPDF)
Purchases & Sales
The biggest module in the system. It records purchases from suppliers and sales to customers, adjusting inventory stock automatically.
Purchase:
- Selection of existing items or creation on the fly
- Assignment of supplier, quantity, unit price, and payment method
- Automatic stock increment
- Recording of the item-supplier relationship with purchase price
Sale:
- Selection of items from inventory with available stock
- Sufficient stock validation before confirming
- Automatic stock decrement
- Transaction recording with payment method
Editing and deletion:
- Quantity modification with differential stock adjustment
- Deletion with full inventory rollback
Modules in detail
Backend
-
Itemmodel (backend/src/models/Item.js): Item CRUD with parameterized SQL queries, name search (ILIKE), associated supplier aggregation withARRAY_AGG, andupdateStockoperation withadd/subtractmode for atomic inventory adjustments. -
Inventorymodel (backend/src/models/Inventory.js): Data access layer for purchase/sale transactions. Includes:createTransaction/createTransactionDetails— header and detail insertionupdateSupplier— upsert of the item-supplier relationship with purchase pricevalidateStock— stock verification before sellinggetPurchases/getSales— queries with JOINs toitem,supplier, andtransaction_itemsoftDeleteTransaction— logical deletion (is_deleted = TRUE)getActiveTransactionsByItemAndSupplier— to decide whether to unlink supplier on deletion
-
Transactionmodel (backend/src/models/Transaction.js): CRUD of financial transactions andgetSummaryfor income/expense balance. -
inventory.service.jsservice: All business logic for the purchase/sale flow:createPurchase— creates or updates items, assigns suppliers, adjusts stockcreateSale— validates stock, creates transaction, deducts inventoryupdatePurchase/updateSale— differential stock adjustment (difference between new and previous quantity)deletePurchase/deleteSale— stock rollback and supplier unlinking
-
Controllers (
inventory.controller.js,item.controller.js,transaction.controller.js): HTTP request handling, validation with Joi, and responses with appropriate status codes. -
Validations (
item.validation.js,transaction.validation.js): Joi schemas for creating and updating items and transactions. -
Routes (
inventory.routes.js,item.routes.js,transaction.routes.js): Endpoint definitions with authentication and authorization middleware.
Implemented endpoints:
| Method | Route | Description |
|---|---|---|
GET | /api/items | List items |
POST | /api/items | Create item |
GET | /api/items/:id | Get item by ID |
PUT | /api/items/:id | Update item |
DELETE | /api/items/:id | Delete item (soft delete) |
GET | /api/inventory/purchases | List purchases |
POST | /api/inventory/purchase | Record purchase |
PUT | /api/inventory/purchase/:id_transaction | Update purchase |
DELETE | /api/inventory/purchase/:id | Delete purchase |
GET | /api/inventory/sales | List sales |
POST | /api/inventory/sale | Record sale |
PUT | /api/inventory/sale/:id_transaction | Update sale |
DELETE | /api/inventory/sale/:id | Delete sale |
Frontend
-
inventory.jsxpage: 3-tab layout (Products / Purchases / Sales) with tab navigation. -
InventoryTable.jsx: Interactive table with search, filters by category and supplier, column sorting, context menu, and modal dialogs for creating, editing, detailing, and deleting. -
PurchasesTable.jsx/SalesTable.jsx: Transaction history tables with currency formatting, localized dates, and export. -
Dialogs:
AddItemDialog,EditItemDialog,ItemDetailsDialog,SellItemDialog,AddPurchaseDialog,EditPurchaseDialog,EditSaleDialog,PurchaseDetailsDialog,SaleDetailsDialog,NewPurchaseForm,ExistingPurchaseForm. -
ExportButtons.jsx: Export of inventory, purchases, and sales to Excel and PDF usingxlsxandjspdf-autotable. -
API layer (
src/api/inventory.js): Axios functions to consume each backend endpoint. -
Frontend validations (
src/validations/): Yup schemas fornewItem,modifyItem,newPurchase,modifyPurchase,newSale,modifySale.
Learnings & challenges
Real-time stock synchronization
The main challenge was ensuring inventory consistency across purchases, sales, edits, and deletions. I solved it with atomic stock adjustments (stock = stock +/- quantity) in SQL, which avoids race conditions. When editing a transaction, the service calculates the difference between the new and previous quantity and adjusts only the delta. Deleting reverts the inventory effect completely.
Item-supplier association with purchase prices
The same item can be purchased from different suppliers at different prices. I implemented an item_supplier intermediate table that records each relationship with its purchase_price and purchase_date. When deleting a purchase, the system checks whether the supplier has other active transactions with that item before unlinking it, avoiding loss of history.
Soft delete with referential integrity
All deletions are logical (is_deleted = TRUE) to preserve history. Queries always include the WHERE is_deleted = FALSE filter, and cascading logical deletions require manual verification in the service before proceeding.
Chilean RUT validation and formatting
I implemented the Chilean RUT validation algorithm (modulo 11 check digit) in both backend and frontend; it uniquely identifies users and suppliers.
References
- Next.js — React framework with Pages Router and SSR.
- Express.js — Minimalist framework for REST APIs in Node.js.
- PostgreSQL — Relational database management system.
- Tailwind CSS — Utility-first CSS framework with variables and dark theme.
- Shadcn/ui — Reusable React components with Radix UI and Tailwind.
- JWT — Bearer authentication token standard.
- PM2 — Process manager for Node.js in production.
- Cloudinary — Cloud-based image storage and management.
Project developed together with Rocío Rivas, Álvaro Loyola and Anaís Saldías.