A modular ERP for growing operations, built in public.
WardSuite is a personal ERP built from frustration with existing tools that are either too expensive, too rigid, or missing the cross-module visibility that operations teams actually need. Starting with CRM and Supply Chain, expanding toward a full business operating system.
| Layer | Technology |
|---|---|
| Frontend | React 19, Vite 6, Tailwind CSS v4 |
| Animations | Motion (Framer Motion v12) |
| State | Zustand 5 (persisted) |
| Backend | Express 4, TypeScript, tsx |
| Database | Firestore (Firebase Admin SDK) β repository pattern, swappable |
| Drag & Drop | @hello-pangea/dnd |
| Tables | TanStack Table v8 |
| Monorepo | NX |
| Build | Vite (web) + esbuild (API) |
| Deployment | Docker + PM2 |
wardsuite/
βββ apps/
β βββ web/ # React SPA (Vite + Tailwind)
β βββ api/ # Express API server
β
βββ libs/
β βββ shared/
β β βββ types/ # @wardsuite/shared/types
β β βββ utils/ # @wardsuite/shared/utils
β β βββ auth/ # @wardsuite/shared/auth (Zustand store)
β β βββ ui/ # @wardsuite/shared/ui (design system)
β β
β βββ crm/
β βββ domain/ # @wardsuite/crm/domain (entities + DTOs)
β βββ data-access/ # @wardsuite/crm/data-access (repos + adapters)
β βββ feature-leads/ # @wardsuite/crm/feature-leads
β βββ feature-customers/ # @wardsuite/crm/feature-customers
β βββ ui/ # @wardsuite/crm/ui
β
βββ CLAUDE.md
βββ tsconfig.base.json
βββ nx.json
βββ Dockerfile
βββ docker-compose.yml
βββ ecosystem.config.js
- Node.js 22+
- Firebase project with Firestore enabled
- Service account key (for Admin SDK)
git clone https://github.com/wardvisual/wardsuite.git
cd wardsuite
npm installCreate .env at the workspace root:
PORT=3000
NODE_ENV=development
FIREBASE_PROJECT_ID=your-project-id
FIREBASE_CLIENT_EMAIL=your-service-account@project.iam.gserviceaccount.com
FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
FIREBASE_DATABASE_ID=
VITE_FIREBASE_API_KEY=
VITE_FIREBASE_AUTH_DOMAIN=
VITE_FIREBASE_PROJECT_ID=
VITE_FIREBASE_STORAGE_BUCKET=
VITE_FIREBASE_MESSAGING_SENDER_ID=
VITE_FIREBASE_APP_ID=
VITE_FIREBASE_DATABASE_ID=npm run dev # API (:3000) + Vite (:5173) in parallel
npm run dev:api # API only
npm run dev:web # Frontend only (proxies /api β localhost:3000)npm run firebase:seed| Password | Role | |
|---|---|---|
| admin@wardsuite.com | admin123 | ADMIN |
| manager@wardsuite.com | manager123 | MANAGER |
| staff@wardsuite.com | staff123 | STAFF |
| Feature | Status |
|---|---|
| Leads | β Kanban + table, CSV import with column mapper, drag-and-drop status |
| Customers | β Full CRUD, convert from lead |
| Pipeline | β Deal Kanban, drag-and-drop stage transitions, revenue totals per stage |
| Activities | β Timeline log with type filters, automatic audit trail |
| Dashboard | β Live revenue stats, 12-month chart, conversion metrics |
| Feature | Status |
|---|---|
| Suppliers | β Supplier management, status tracking |
| Products | β Product catalog with stock levels and reorder alerts |
| Purchase Requests | β Request lifecycle management |
| Stock Movements | β Inbound/outbound inventory tracking |
- Lead CRUD with kanban and status pipeline
- Lead-to-customer conversion with audit trail
- Customer full CRUD
- Deal pipeline with drag-and-drop stage management
- Activities/timeline log
- Dashboard with live revenue analytics
- Automatic audit trail on all CRM mutations
- Lead scoring (Low / Medium / High / Hot)
- Customer segments / tags
- Deal probability % and weighted pipeline value
- Bulk lead import validation and error reporting
- Lead assignment between team members
- Deal close date notifications / overdue alerts
- Full-text search across leads, customers, deals
- Revenue forecast view
- Win/loss reason tracking
- Deal history log
- Duplicate lead detection on import
- Customer LTV calculation
- In-app notifications
- Email integration (SendGrid / Resend)
- Activity reminders
- CRM analytics (conversion funnel, lead source breakdown)
- Exportable reports (PDF / CSV)
- Team performance dashboard
- Role-based field visibility
- Invite by email
- Password change and session management
- 2FA (TOTP)
Services code against interfaces, not concrete classes. Swapping Firestore for Postgres means implementing the interface β no changes to service code.
export interface ILeadRepository {
findAll(): Promise<Lead[]>;
create(dto: CreateLeadDto, actorId?: string): Promise<Lead>;
}
// Today
const repo = new FirestoreLeadRepository(db);
// Future
const repo = new PostgresLeadRepository(pgClient);Every CRM mutation (create, update, delete, stage change) is logged automatically:
Lead "Acme Corp" (LD-042) created from source: referral.
Deal "Q3 Renewal" moved from "proposal" β "won".
Customer "TechBase Ltd" updated β status "inactive" β "active".
POST /api/auth/login β { token, user }
β stored in Zustand (localStorage persist)
β injected as Bearer token on every request
β 401 auto-clears session + redirects to /login
# Docker
npm run docker:build
npm run docker:up
# PM2
npm run build
npm run start:pm2| Script | Description |
|---|---|
npm run dev |
API + Vite dev servers in parallel |
npm run build |
Build frontend + bundle API |
npm run lint |
TypeScript type check |
npm run firebase:seed |
Seed Firestore with demo data |
npm run docker:up |
Start Docker containers |
npm run start:pm2 |
Start with PM2 in cluster mode |
Eduardo β @wardvisual
MIT Β© 2025 Eduardo
