init local package repo

This commit is contained in:
MacPro
2026-02-27 12:44:50 -06:00
commit 7027efd777
34 changed files with 20093 additions and 0 deletions
+258
View File
@@ -0,0 +1,258 @@
# Venture Fund CRM — Project Context for Claude
You are continuing development on a self-hosted CRM system for a venture fund. Below is everything you need to know about what has been built, how it works, and what comes next.
---
## Business Context
- **Fund:** ~$200M AUM, currently fundraising for Fund II
- **Users:** Team of 5 people, accessing via browser on local network or remotely via Tailscale VPN
- **Current LPs:** 150 investors
- **Prospects:** 250+ being tracked
- **Migrating from:** Airtable (CSV exports available)
- **Core goals:**
1. Eliminate sensitive LP/prospect data from third-party servers (Airtable, CRMs)
2. Stop paying monthly subscription costs
3. Purpose-built tool for fundraising workflow: managing existing investors, tracking new prospects, raising capital
- **User:** Grant (grant@ten31.xyz)
---
## What Has Been Built (Sprint 1 — Complete)
A fully functional prototype with backend API, frontend UI, demo data, and utility scripts. Everything runs locally with zero external dependencies beyond two Python packages.
### Tech Stack (Actual — differs from original plan)
The original plan called for FastAPI + SQLAlchemy + separate React build, but the build environment lacked pip/npm access. The stack was adapted to:
- **Backend:** Python 3 stdlib HTTP server + `sqlite3` + `bcrypt` + `PyJWT` — single file, no framework
- **Database:** SQLite with WAL mode (concurrent reads, serialized writes — fine for 5 users)
- **Frontend:** Single self-contained HTML file loading React 18 + Babel from CDN (unpkg)
- **Deployment:** Run `python3 backend/server.py` — serves both API and frontend on port 8080
- **Remote access:** Tailscale mesh VPN (each device gets a private IP, peer-to-peer encrypted)
### Project Structure
```
venture-crm/
├── backend/
│ ├── server.py # Complete API server (1,873 lines)
│ └── requirements.txt # bcrypt, PyJWT (for reference)
├── frontend/
│ └── index.html # Complete React SPA (2,982 lines)
├── data/
│ └── crm.db # SQLite database (created on first run)
├── scripts/
│ ├── create_user.py # CLI tool to add users
│ ├── reset_password.py # CLI tool to reset passwords
│ └── backup.sh # Database backup with 30-day retention
└── start.sh # Launch script
```
### Database Schema
All tables use TEXT primary keys (8-char UUIDs). The database is at `data/crm.db`.
**Tables:**
- `users` — id, username, email, password_hash, full_name, role (admin/manager/member), is_active
- `contacts` — id, first_name, last_name, email, phone, mobile, title, organization_id (FK), contact_type (investor/prospect/advisor/other), status, source, tags (JSON), notes, linkedin_url, preferred_contact, created_by (FK)
- `organizations` — id, name, type, industry, website, phone, email, address, city, state, country, description, tags (JSON), created_by (FK)
- `opportunities` — id, name, contact_id (FK), organization_id (FK), stage (lead/outreach/meeting/due_diligence/committed/funded), commitment_amount, expected_amount, probability, expected_close_date, fund_name, description, next_step, owner_id (FK), priority (low/medium/high), lost_reason
- `communications` — id, contact_id (FK), opportunity_id (FK), type (email/call/meeting/note/text), subject, body, communication_date, duration_minutes, outcome, next_action, next_action_date, attendees (JSON), created_by (FK)
- `lp_profiles` — id, contact_id (FK, unique), commitment_amount, funded_amount, commitment_date, fund_name, investor_type, accredited, legal_docs_signed, signed_date, wire_received, wire_date, k1_sent, preferred_communication, notes
- `custom_fields` — id, name, entity_type, field_type, options (JSON), required, display_order
- `custom_field_values` — id, custom_field_id (FK), entity_id, entity_type, value
- `audit_log` — id, user_id (FK), entity_type, entity_id, action, changes (JSON), created_at
- `tags` — id, name (unique), color
**Key indexes:** contacts(contact_type, status, organization_id), opportunities(stage, owner_id, contact_id), communications(contact_id, communication_date), audit_log(entity_type, entity_id), lp_profiles(contact_id)
### API Endpoints
All endpoints except auth require `Authorization: Bearer <jwt_token>` header. Server runs at `http://0.0.0.0:8080`.
**Auth:**
- `POST /api/auth/login` — body: {username, password} → {token, user}
- `POST /api/auth/register` — body: {username, password, email, full_name} → {token, user}
**Contacts:**
- `GET /api/contacts?type=&status=&search=&sort=&order=&limit=&offset=&organization_id=&tag=` → {data[], total, limit, offset}
- `GET /api/contacts/:id` → {data: {contact + communications[], opportunities[], lp_profile}}
- `POST /api/contacts` — full CRUD
- `PUT /api/contacts/:id`
- `DELETE /api/contacts/:id`
**Organizations:**
- `GET /api/organizations?search=&type=&limit=&offset=` → {data[], total}
- `GET /api/organizations/:id` → {data: {org + contacts[], opportunities[]}}
- `POST /api/organizations` — full CRUD
- `PUT /api/organizations/:id`
- `DELETE /api/organizations/:id`
**Opportunities (Pipeline):**
- `GET /api/opportunities?stage=&owner_id=&search=&priority=&fund_name=&limit=&offset=` → {data[], total}
- `GET /api/opportunities/:id` → {data: {opp + communications[], stage_history[]}}
- `POST /api/opportunities`
- `PUT /api/opportunities/:id`
- `PATCH /api/opportunities/:id/stage` — body: {stage} (logs stage change in audit)
- `DELETE /api/opportunities/:id`
**Communications:**
- `GET /api/communications?contact_id=&type=&search=&limit=&offset=` → {data[], total}
- `GET /api/contacts/:id/communications` → same as above, scoped to contact
- `POST /api/communications`
- `PUT /api/communications/:id`
- `DELETE /api/communications/:id`
**LP Profiles:**
- `GET /api/lp-profiles?fund_name=&search=` → {data[], total}
- `GET /api/lp-profiles/:id` → {data}
- `POST /api/lp-profiles` — also sets contact type to 'investor'
- `PUT /api/lp-profiles/:id`
**Reports:**
- `GET /api/reports/dashboard` → {metrics, pipeline_stages[], recent_communications[], upcoming_actions[], recent_stage_changes[]}
- `GET /api/reports/pipeline` → {by_stage[], by_owner[], by_priority[]}
- `GET /api/reports/lp-breakdown` → {lps[], summary, by_type[]}
- `GET /api/reports/activity?days=30` → {by_user[], by_day[]}
**Import/Export:**
- `POST /api/import/csv` — body: {data: [...objects], entity_type, mapping: {csv_col: crm_field}, dry_run: bool}
- `GET /api/export/contacts` → {data[]}
**Other:**
- `GET /api/tags` / `POST /api/tags`
- `GET /api/users`
- `GET /api/audit-log?entity_type=&entity_id=`
- `GET /api/health`
### Frontend Pages
The frontend is a single HTML file with inline CSS (dark theme) and React via CDN. Pages:
1. **Login** — username/password form, registration option
2. **Dashboard** — KPI cards (Total LPs, Committed $, Pipeline Value, Active Opportunities, Prospects, Monthly Comms), pipeline stage visualization, recent communications, upcoming actions, recent stage changes
3. **Contacts** — tabbed (All/Investors/Prospects), searchable sortable table, slide-over detail panel with communications timeline and opportunities, add/edit modal
4. **Pipeline** — Kanban-style board (Lead → Outreach → Meeting → DD → Committed → Funded), stage summary bar with $ per stage, opportunity cards with stage selector, add/edit modal
5. **Communications** — chronological list, filter by type/contact, log new communication form
6. **LP Tracker** — summary cards (Total Committed, Funded, Avg Check, LP Count), table with status indicators (checkmarks) for docs/wire/K1
7. **Import** — CSV paste/upload, preview table, field mapping interface, dry-run validation, execute import
8. **Settings** — user profile, tag management
### Demo Data (Seeded Automatically)
On first run, the server seeds:
- 2 users: `admin`/`admin123` (admin role), `grant`/`password` (admin role)
- 8 organizations (Sovereign Wealth Holdings, Pacific Capital Partners, Northeast Pension Fund, Redwood Endowment, Atlas Family Office, Summit Insurance Group, Cascade Wealth Management, Blue Harbor Foundation)
- 12 contacts (6 investors, 6 prospects)
- 6 LP profiles totaling $83M committed (all Fund I, all fully funded)
- 6 pipeline opportunities totaling $40M expected (Fund II prospects at various stages)
- 8 communication records (emails, calls, meetings)
- 6 tags (High Priority, Fund I LP, Fund II Prospect, Family Office, Institutional, Re-up Target)
### How to Run
```bash
pip3 install bcrypt PyJWT
cd venture-crm
python3 backend/server.py
# Open http://localhost:8080
# Login: grant / password
```
### What Has Been Tested
All API endpoints have been verified via curl:
- Auth (login, register)
- Contact CRUD + search
- Organization CRUD
- Opportunity CRUD + stage changes
- Communication CRUD
- LP profile CRUD
- Dashboard, pipeline, LP breakdown reports
- CSV import with dry-run and field mapping
- Frontend serves correctly from the backend
---
## What Has NOT Been Built Yet (Remaining Sprints)
### Sprint 2 items still needed:
- Custom fields UI (backend schema exists but not wired to frontend forms)
- Drag-and-drop on pipeline board (currently uses dropdown stage selector)
### Sprint 3: Airtable Migration + Custom Fields
- Custom field definition admin UI
- Display custom fields on contact/opportunity forms
- Actual Airtable data migration (import wizard exists but hasn't been used with real data)
### Sprint 4: Reporting + Polish
- Pipeline analytics (deal velocity, conversion rates between stages)
- User activity report page
- CSV export buttons on all reports
- Bulk actions on contact list (tag multiple, assign, bulk export)
- Automated daily backup via cron
- Team setup documentation
### Future Enhancements (discussed but not planned):
- Email integration (auto-log emails via IMAP)
- Calendar sync
- Task assignments linked to opportunities
- Bulk email with templates
- Two-factor authentication
- Advanced saved search filters
- Audit trail UI page
---
## Architecture Decisions & Constraints
1. **Single-file backend:** The Python server is one file (`server.py`) using stdlib `http.server`. No framework. This keeps deployment dead simple but means no middleware pattern, no auto-docs, no async. If the codebase grows significantly, consider migrating to FastAPI.
2. **Single-file frontend:** The React app is one HTML file loading from CDN. No build step. This means no TypeScript, no tree-shaking, no code splitting. Babel compiles JSX in the browser. If the UI grows significantly, consider splitting into a proper Vite/React project.
3. **SQLite WAL mode:** Handles 5 concurrent readers + 1 writer. Fine for this team size. If the team grows past 10-15, migrate to PostgreSQL.
4. **No localStorage:** JWT token stored in React state only (memory). Page refresh = re-login. This is intentional for security.
5. **8-char UUIDs:** Generated via `uuid.uuid4()[:8]`. Collision probability is negligible at this data scale.
6. **Tailscale for remote access:** Server binds to 0.0.0.0. Tailscale gives each device a 100.x.x.x IP. No port forwarding, no public exposure.
---
## Key Files to Read
When making changes, these are the files:
- **`backend/server.py`** (1,873 lines) — ALL backend logic: database schema, auth, every API endpoint, seed data, server startup. Search for handler method names like `handle_list_contacts`, `handle_create_opportunity`, etc.
- **`frontend/index.html`** (2,982 lines) — ALL frontend logic: CSS styles, React components, API client, every page. Search for component names like `Dashboard`, `ContactsPage`, `PipelinePage`, etc.
- **`scripts/create_user.py`** — CLI to add team members
- **`scripts/backup.sh`** — Database backup with rotation
- **`start.sh`** — Launch script
---
## Common Modification Patterns
**Adding a new field to contacts:**
1. In `server.py`: add column to CREATE TABLE, add to INSERT/UPDATE in handler methods
2. In `index.html`: add field to the contact form component and detail view
3. Delete `data/crm.db` to recreate schema (or use sqlite3 ALTER TABLE)
**Adding a new pipeline stage:**
1. In `server.py`: add to `PIPELINE_STAGES` list
2. In `index.html`: add to the stages array in the Pipeline component
**Changing the color scheme:**
1. In `index.html`: modify the CSS variables in the `<style>` tag (search for hex colors like `#0f172a`, `#1e293b`, `#6366f1`)
**Adding a new API endpoint:**
1. In `server.py`: add route matching in `do_GET`/`do_POST`/etc., then add handler method
**Adding a new page:**
1. In `index.html`: create a new component, add it to the navigation sidebar and the page router