Behind the build
Hunt Planner Pro
A productized planning-and-booking experience for guided hunts, built as a transferable commercial asset.
Product architecture, designed for handover to a new owner.
- React
- TypeScript
- Vite
- Typed content model
- Supabase (integration seam)
Hunt Planner Pro started with a simple commercial idea:
Planning an international hunting trip should not require jumping between outfitter websites, trophy lists, destination pages, email threads, and scattered travel advice just to answer one question:
What hunt actually fits me?
I built Hunt Planner Pro as a productized planning and booking experience for guided hunting destinations, initially covering South Africa, Canada, and New Zealand.
Unlike my other projects, there is another part of this story worth mentioning.
Hunt Planner Pro was built as a commercial digital asset. The product is currently offered for sale, and acquisition discussions are in progress.
That changed some of my engineering priorities.
I was not designing software that only I could understand or operate. I wanted the product, content model, routing, and extension points to be understandable enough that another owner could acquire it and continue developing it.
Architecture at a Glance
Hunter
│
▼
Hunt Planner Pro
│
React + TypeScript
Vite SPA
│
┌───────────────┼────────────────┐
│ │ │
▼ ▼ ▼
Regions Hunts Trip Planning
│ │ │
└─────── Typed Content ──────────┘
src/data/
│
┌───────────┼───────────┐
▼ ▼ ▼
Discovery Booking HuntAsk
workflow assistant
│
▼
Supabase
integration seam
One architectural decision sits underneath most of the product:
The hunting catalog is structured data, not page content scattered throughout React components.
That sounds small.
It is what makes the site extensible.
Case Study 1: Building a Travel Product Without Building Every Destination Again
The problem
International hunting is inherently heterogeneous.
South Africa is not Canada.
Canada is not New Zealand.
Even inside one destination, a plains-game safari, dangerous-game hunt, or species-specific package can require very different content.
A naïve implementation would create a collection of one-off pages:
SouthAfricaPage.tsx
CanadaPage.tsx
NewZealandPage.tsx
KuduPage.tsx
MoosePage.tsx
...
That works quickly.
It also makes every new destination an engineering project.
The constraint
The product needed enough flexibility to tell a rich destination-specific story while still having a common catalog that could support search, filtering, linking, SEO, and booking.
I did not want a CMS dependency simply to make the content editable.
At this stage, the product benefits more from strongly structured content under source control than from another runtime system.
The decision
I treated content as typed application data.
The canonical hunt catalog lives in TypeScript modules rather than inside presentation components.
src/data/
│
├── hunts.ts
│ ├── regions
│ └── Hunt[]
│
└── southAfrica/
├── sections/
├── packages/
├── types.ts
└── index.ts
A hunt has a stable slug and structured attributes.
Routes resolve that data:
/region/:slug
/hunt/:slug
Generic regions use the common rendering model.
But I deliberately avoided forcing every destination into exactly the same presentation.
South Africa has a richer composition built from separately typed sections and packages.
So the architecture supports both:
shared domain model
+
destination-specific storytelling
rather than choosing one at the expense of the other.
Why that matters
Adding another hunt should primarily be a content operation.
It should not require inventing another routing scheme or duplicating application behavior.
At the same time, a commercially important destination can have a substantially richer experience without corrupting the generic catalog model.
The boundary is:
Normalize the domain. Do not unnecessarily normalize the story.
The outcome
The product now has a reusable catalog and routing structure across multiple hunting regions while retaining room for high-value destination-specific experiences.
The same structured content also feeds page metadata and canonical URLs, which means SEO is part of the content model rather than something added independently after each page is built.
For an eventual buyer, that makes expansion understandable:
new hunt
→ add structured hunt data
new conventional region
→ reuse region architecture
strategic destination
→ add richer composed content
The architecture establishes that growth path without requiring a CMS or backend dependency before either becomes necessary.
Case Study 2: Carrying Intent From Discovery Into Booking and Assistance
The problem
One of the easiest ways to make a booking product feel fragmented is to forget everything the visitor just did.
A person can spend considerable time choosing a destination and package, complete a booking flow, receive a reference number, and then open the help interface only to be greeted with:
How can I help you?
Technically the components work.
Experientially the system has lost the conversation.
The decision
I treated the booking result as context that other parts of the application could consume.
The current booking workflow is deliberately lightweight and client-side:
Select package
↓
Booking checkout
↓
Generate booking reference
↓
Store booking context
↓
Confirmation
↓
HuntAsk receives the same context
The booking context is stored for the browser session and contains enough information for the assistance experience to recognize that this is no longer an anonymous visitor casually browsing.
The HuntAsk widget reads that context and can open from other parts of the interface through a custom application event.
That keeps the components decoupled.
The booking component does not need to know how the chat interface renders itself.
It communicates intent:
open-huntask
and the assistant owns its own UI behavior.
The important boundary
The current implementation is intentionally not presented as something it is not.
There is no production payment backend yet.
Booking and contact submissions are not currently persisted.
The existing Supabase client establishes the backend integration seam, but UI workflows have not yet been wired to it.
HuntAsk is currently a deterministic FAQ assistant using keyword matching rather than an LLM.
Those are deliberate boundaries in the product’s present stage, not capabilities I would hide behind marketing language.
Why I kept it that way
The product needed to establish the customer journey before accumulating backend machinery.
discover
↓
compare
↓
choose
↓
book
↓
prepare
↓
ask for help
Once that interaction model is proven, persistent bookings, payments, CRM integration, authenticated customers, and an LLM-backed planning assistant have obvious insertion points.
I prefer that sequence to building expensive infrastructure around a customer journey that has not yet earned it.
Designing for Transfer, Not Just Development
Hunt Planner Pro has one engineering constraint that most portfolio projects do not:
I may not be the person operating it long term.
Because the product is being offered for sale, architecture that depends on undocumented personal knowledge reduces the value of the asset.
That makes boring decisions valuable.
Routes are explicit.
Content has canonical sources.
Slugs have defined meaning.
Destination data is separated from rendering.
UI primitives follow a component system.
SEO behavior has a common hook.
The Supabase integration boundary already exists even though persistence has not yet been switched on.
The product can therefore be acquired in its current form and extended incrementally rather than requiring an immediate rewrite.
What I Would Build Next
The current architecture also makes the next engineering steps fairly clear.
A production booking implementation would move the booking authority out of browser memory and into durable server-side persistence.
Payments would become a separate transactional boundary rather than another React checkout step.
Contact and booking submissions would become durable records.
HuntAsk could evolve from deterministic FAQ matching into a grounded planning assistant using the hunter’s selected destination, species, package, travel constraints, and booking context.
That progression would look roughly like:
Today
React
↓
typed catalog
↓
client booking context
↓
rule-based HuntAsk
Next
React
↓
booking API
↓
persistent booking
↓
payment boundary
↓
customer/trip context
↓
grounded HuntAsk
The important part is that the current application does not have to be discarded to get there.
Why I Built It This Way
Hunt Planner Pro is not an exercise in maximizing technical complexity.
It is an exercise in product architecture.
I wanted to determine what should become a reusable domain model, what deserved a specialized experience, what needed persistence now, what could remain local for the current product stage, and where future integration boundaries should exist.
That is also why I am comfortable showing what has not been built yet.
Software does not become more credible because every box in an architecture diagram contains another service.
The better question is whether each boundary exists for a reason.
With Hunt Planner Pro, the goal was to create a coherent commercial product, make its next steps technically obvious, and leave behind an asset another operator could realistically take over.
The product is currently available for acquisition, with negotiations in progress.
More things I've built
- ClinSupplyCompass Live · multi-tenant SaaS
Closed-loop clinical-supply planning, proven against the 130-sheet workbook it replaced.
- HeyLayla Live · free pilot
A private, verified matrimonial platform: a public site, a member PWA, and an operator console over one API.
- Care Partners at Home Pre-launch
A multi-tenant operating platform for a home-care agency: operator, caregiver, and family, over one auditable workflow.