Building a Construction RFC Tracker in 5 Days
MagicLog is a Next.js + Convex app for construction project document management. I prompted Claude to build it over 5 days: Excel import, PDF upload, audit trails, real-time notifications, and client-facing share links.
Status update (April 2026): The code is live but the project isn't in real use yet. My dad hasn't migrated his real project data in. This post documents the 5-day build; whether it actually replaces his spreadsheets is still TBD.
The Problem: Construction project managers track hundreds of RFCs (Requests for Clarification), submittals, and change orders across multiple projects in spreadsheets. Missing RFC deadlines can delay $40M+ builds. Missing change orders can mean $200K+ cost overruns. Spreadsheets have no audit trails, version control, or deadline alerts.
The Build: My dad, a project manager, asked if I could build something better. I opened Claude Code and went.
The Problem
Construction project management has a document tracking problem. A single hospital project generates:
- 50–100 RFCs — questions from contractors that need architect responses within contractual deadlines
- 200+ submittals — shop drawings, material specs, and samples that need review and approval
- 20–50 change orders — scope changes with cost implications that need tracking through approval chains
These documents flow between architects, contractors, owners, and engineers. Each has deadlines. Each has status. Many have dollar amounts attached. When they live in Excel, things slip through the cracks.
Existing tools (Procore, PlanGrid, Bluebeam) are designed for general contractors — not architectural PMs. They're expensive, bloated, and don't match how an architect actually tracks their obligations.
The Stack
- Next.js 15 with App Router and TypeScript
- Convex for the real-time database and server functions
- Tailwind CSS for styling
- Clerk for auth (with demo mode fallback)
- Vercel for deployment
I chose Convex because I've shipped Linesheet on it and know the patterns cold. Real-time reactivity means status changes appear instantly across tabs — no refresh needed. The mutation/query model with TypeScript end-to-end means I catch schema mismatches at compile time, not in production.
Day 1: Schema and Seed Data
The first commit was the data model. Seven tables: projects, rfcs, submittals, ccds (change orders), documents, obligations, and notifications. Every table has organizationId for multi-tenant isolation and by_org indexes.
I wrote 51KB of seed data — three realistic construction projects with varied statuses, priorities, dollar amounts, and dates. Riverside Medical Center (active, 120 days in), Pacific Plaza Office Tower (active, 100 days in), and Westbrook Library Renovation (recently completed). This isn't placeholder data — the RFCs reference real construction scenarios like "structural steel moment connection clarification" and "HVAC ductwork routing conflict at Level 3."
Good seed data is worth more than good UI early on. It exposes schema gaps, makes the dashboard feel real during development, and gives you something to demo before real data exists.
Day 2: The Big Feature Commit
Auth, CRUD, Excel import, PDF upload, notifications, and the urgency dashboard — all in one commit. Here's why that works:
Auth: Every Convex mutation starts with requireAuth(ctx) returning { userId, orgId }. Demo mode falls back to demo-org-001 when no Clerk keys are configured, gated behind an environment variable so it can't accidentally leak into production.
CRUD: Full create/update/delete for all record types with form dialogs and confirmation modals. Status and priority are editable inline — click a badge, pick from a dropdown, mutation fires immediately. No save button needed.
Excel Import: This is the killer feature for adoption. My dad's data lives in Excel. The import system:
- Drag-and-drop an
.xlsxfile - Auto-detects sheet type (RFC/Submittal/CCD) from column headers
- Shows a column mapping UI for non-standard headers
- Previews the first 10 rows
- Validates every row against the schema
- Reports errors per-row with details
- Batch-inserts up to 500 rows
I replaced the xlsx library (which had CVEs) with ExcelJS. The column mapping is critical — every firm's spreadsheet format is slightly different.
Urgency Dashboard: Three tiers, color-coded:
- Critical (red) — overdue items
- Warning (amber) — due within 7 days
- On Track (green) — everything else
The dashboard shows stats cards, project progress bars, and an activity feed. The urgency panel is what makes this useful — at a glance, you know what needs attention today.
Day 3: Audit Trail and Import Tracking
Two features that separate a toy from a tool:
Audit trail: Every mutation to an RFC, submittal, or CCD logs who changed what and when. Construction is contractual — "when did we respond to RFC-023?" is a question that comes up in disputes. The audit trail answers it.
Import tracking with undo: The saga pattern — each import gets a unique ID, a progress bar during bulk inserts, and a history page showing past imports. If you import the wrong file, you can undo the entire batch. This is the kind of feature that's invisible until you need it, and then it saves your day.
Day 4: Notifications and Search
Notifications: A cron job runs daily, checking due dates across all record types. Items due within 7 days get a notification. Overdue items get escalated. The bell icon in the header shows unread count with a dropdown. Mark-as-read, mark-all-as-read.
I built graduated urgency into the notification system — 4 tiers with anti-windup. An item that's been overdue for 30 days doesn't keep re-notifying at the same priority. It escalated once and stays there.
Full-text search: Convex's built-in search index across all record types. Filter by type, filter by project. Results link directly to the project detail page.
Day 5: Security Audit and Deploy
The last day was hardening:
- Gated demo mode behind
DEMO_MODEenvironment variable - Audited every mutation for org isolation
- Made
searchIndex.addChunkaninternalMutation(not callable from clients) - Verified file upload ownership checks
- Optimized search indexes with type filtering and org scoping
Then: npx convex deploy, push to GitHub, Vercel auto-deploys. The code is running — but "live" in the sense of real users using it on real projects hasn't happened yet.
What I'd Do Differently
Start with the import flow, not the CRUD. My dad's adoption path is: import existing Excel → see it rendered nicely → start making edits. The CRUD forms are secondary to the import experience. I built CRUD first because it's the natural order, but if I were optimizing for time-to-value, I'd flip it.
Mobile-first for the dashboard. The responsive design works (hamburger menu, stacking grids), but my dad checks this on his phone at job sites. The dashboard cards should be bigger touch targets with more prominent urgency indicators. That's a polish pass, not a rebuild.
The Real Lesson
The speed came from three things:
-
Convex patterns I already knew. Tenant isolation, auth guards, batch operations, cron jobs — I've written these patterns dozens of times in Linesheet. No time spent figuring out the framework.
-
Realistic seed data on day 1. Every page had real content to test against from the start. I never built UI against empty states and then discovered it broke with real data.
-
Knowing when to stop. MagicLog doesn't have role-based access control, granular permissions, email notifications, or a mobile app. It has the features my dad actually needs to stop losing track of RFCs. Ship the 80% that matters, iterate on the rest.
The plan was: demo March 18, my dad imports his real project data, I find out if the column mapping is flexible enough and the urgency thresholds are calibrated right. As of April 2026 that hasn't happened yet. The code is ready; the adoption isn't. Whether a tool built in 5 days can replace spreadsheets that have (barely) worked for 20 years is still an open question.