See it in action
The diary is the operational backbone of the tracker — every meeting, decision, and milestone logged in one place. Click "Diary" in the nav to see it.
Click the Diary tab in the nav above to see the diary view.
The heatmap shows you at a glance which companies are active and which have gone quiet. Below is how to build it.
Build it with Claude
If you've already built the Portfolio Tracker, the diary is an extension of the same codebase. If you're starting fresh, these prompts include everything you need.
Set up diary entries
Build the core component to fetch and display entries for a single company.
"Build a CompanyDiary page component for my portfolio tracker. It should fetch a single company by ID along with all its diary entries from Supabase. Display a header with company name, status, category, and investment year. Below that, show the diary settings: update cadence, last update date, founder name and email, and internal notes (only visible to syndicate leads). Then render all entries in a timeline format, reverse chronological, with color-coded badges for each entry type (update=blue, funding=green, closed_round=purple, warning=red)."
Build the heatmap view
Add entry creation and wiring to the dashboard heatmap.
"Add a 'New Entry' form to the CompanyDiary page. Only show it if the user is_syndicate_lead. Fields: date (default today), entry_type (dropdown: update, funding, closed_round, warning), title (text), body (textarea). On submit, insert into diary_entries via Supabase and refresh the timeline. The database trigger will auto-update last_update_date in company_diary_settings."
Add the company detail page
Link the heatmap cells to the company diary page.
"On the Dashboard heatmap, make each company name a link to /company/:id which renders the CompanyDiary component. Add a back button on the CompanyDiary page that returns to the dashboard. When navigating from a heatmap cell, scroll to the entry for that month if possible."
Wire up auto-updates
Scope the investor view and set up database triggers.
"Add investor scoping to the CompanyDiary page. Check if the current user is_syndicate_lead. If not, hide: internal_notes, founder_name, founder_email, and the new entry form. RLS already filters which companies they can see — this is just hiding the lead-only UI elements. Also add a header that says 'Your Portfolio' instead of 'All Companies' for investor users."
Deploy it
The diary deploys as part of the same React app as the portfolio tracker — they're routes in the same SPA. Same Cloudflare Pages setup, same GitHub repo, same auto-deploy pipeline.
/ → Dashboard (heatmap + stats)
/company/:id → CompanyDiary (timeline + settings)
/login → Magic link auth
If you've already deployed the portfolio tracker, the diary is already there — it's just the company detail page. If you're starting from scratch, follow the deploy steps in the Portfolio Tracker guide.
Under the hood
Everything above gets you a working diary. Below is how it works — useful for customisation.
Entry types
Each entry has a date, a type, a title, and a body. The type determines how it shows up in the timeline and on the heatmap.
update — Regular portfolio update from the founder
"Q1 revenue up 40%, expanding to 3 new markets"
funding — Funding round activity
"Series A term sheet from Lead VC, $8M at $35M pre"
closed_round — A round that closed
"Seed round closed, $2.1M total, 12 investors"
warning — Something that needs attention
"CTO departed, replacement search underway"
Auto-update trigger
One of the most useful pieces: a database trigger that automatically updates last_update_date whenever a new entry is created. This means overdue detection is always current without manual bookkeeping.
CREATE OR REPLACE FUNCTION update_last_diary_date()
RETURNS trigger AS $$
BEGIN
UPDATE company_diary_settings
SET last_update_date = NEW.entry_date
WHERE company_id = NEW.company_id
AND (last_update_date IS NULL
OR NEW.entry_date > last_update_date);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER diary_entry_update_date
AFTER INSERT OR UPDATE ON diary_entries
FOR EACH ROW
EXECUTE FUNCTION update_last_diary_date();
This trigger fires on every insert or update to diary_entries. It only moves the date forward (never backward), so editing an old entry won't mess up the overdue calculation.
The company detail page
When you click into a company, you see its full diary — every entry in reverse chronological order, plus metadata like status, category, investment year, update cadence, and founder contact info. Internal notes (visible only to the syndicate lead) sit at the top.
What the company page shows
Header: Company name, status badge, category, investment year. Settings: Update cadence, last update date, founder name and email, internal notes. Timeline: Every diary entry with color-coded type badges, dates, and full body text. Entries are grouped visually — you can scan the colors to see the company's trajectory at a glance.
Making it yours
The diary we built for Raspberry Ventures tracks 27 companies with 83 entries. But the structure works for any size. A few ways to customize it:
- Add custom entry types. We use four (update, funding, closed_round, warning). You might want "board_meeting", "exit", "write_off", or "follow_on".
- Change the cadence options. We use monthly/quarterly/semi-annual/annual. If your portfolio is mostly early-stage, you might want weekly as an option.
- Add file attachments. Supabase Storage can hold pitch decks, term sheets, and LP reports alongside diary entries.
- Build email digests. Use Supabase Edge Functions to send weekly or monthly summaries of new entries to your investors.
- Add a search. Once you have hundreds of entries, full-text search across all diary entries becomes valuable. Supabase supports PostgreSQL full-text search natively.
Related reading
The diary and the portfolio tracker share the same database and follow the same pattern. See the Portfolio Tracker guide for the full context on how these tools connect, the database schema, and the Supabase + React + Cloudflare Pages pattern.