Back to projects
Apr 01, 2026
4 min read

Vital Registry v2: React Production

Full React migration of Vital Registry, the production CRM built with my brother for our Mum's BEMER medical device rental business. Same Firebase + Stripe backbone, redesigned UI, dev/UAT/prod deployment pipeline, and live at vital-registry.com/contracts.

The React rewrite of Vital Registry. Same business logic as v1 (Streamlit version), redesigned from scratch in React + TypeScript, and running in production at vital-registry.com/contracts.

Live production app — non-technical user (our Mum) runs her BEMER rental business on it daily.

Why a v2

v1 shipped fast on Streamlit because rendering a working UI in Python was the shortest path to a production app for a non-technical user. It worked — but Streamlit’s page-rerun model and limited component ergonomics made mobile responsiveness and UX polish a constant fight. v2 is the same domain, same rules, done right in React.

What changed

  • Frontend: React + TypeScript replaces Streamlit’s server-rendered UI. Component boundaries are explicit, state is local, and the mobile view is first-class.
  • Backend: Firebase Firestore stays. The v1 data model, including the dual-inventory Possession Envelope Rule, ports directly — core business logic was already centralised in the DAO layer.
  • Auth: Firebase Auth with Google/Facebook OAuth, migrated from v1.
  • Billing: Stripe Checkout + Customer Portal, plan-based feature gating preserved.
  • Multi-environment pipeline: dev / UAT / prod Firebase projects, deploy gates, separate Stripe keys per env. v1 ran on a single Streamlit Cloud instance; v2 is structured for real release management.

Shared with v1

Every business rule that was explicitly covered by the 391-test suite in v1 maps to v2:

  • Dual inventory — owned vs partner assets, with the Possession Envelope Rule enforced before form submission.
  • Multi-entity billing — rental and sale transactions tied to specific legal entities, with validation against deleted entities.
  • Optimistic UI pattern — ported from Streamlit’s st.session_state.task_overrides idea to React state, so task completions feel instant without round-trip to Firestore.
  • Client-side PDF contracts — same html2pdf approach, now using React’s component tree to build the HTML before passing to the PDF renderer.

Tech Stack

Layerv1 (Streamlit)v2 (React)
UIStreamlitReact + TypeScript
State@st.cache_data + st.session_stateLocal component state + React Query
DatabaseFirebase FirestoreFirebase Firestore (same schema)
Authgoogle-auth-oauthlibFirebase Auth SDK
PaymentsStripe API via PythonStripe API via TypeScript SDK
PDFhtml2pdf.js injectedhtml2pdf.js native
DeploymentStreamlit Cloud (single env)Firebase Hosting (dev/UAT/prod)

Built With

Co-developed with my brother Zsombor, who set the visual system and component language. I did almost all of the frontend redesign on top of it, plus the data-layer port from v1, multi-environment deployment setup, and the test harness that carries over from v1.

Case study: czaban.dev/portfolio (MedKölcsön v2).

Running it in production

The app runs across three environments gated by GitHub Actions: feature branches to dev, PRs to UAT, main to prod. All wired up on the Firebase Spark (personal) plan with Gitleaks on every run and weekly scheduled CVE scans. Full story: Three Environment GitOps on a Firebase Personal Plan.

With 20 active users and hard Spark plan quota limits, the monitoring setup is simple but deliberate: PostHog WAU tracking, Firebase alerts that fire before the limits hit, and one rule — know your quota headroom, WAU, and error rate before your users do. Details: The Three Numbers That Matter: PostHog + Firebase Monitoring.