From b16472e8b95a89c75699e5dc9faaba3fe51f2c29 Mon Sep 17 00:00:00 2001
From: raphou00 <105916427+raphou00@users.noreply.github.com>
Date: Thu, 19 Mar 2026 23:34:03 +0100
Subject: [PATCH] docs: improve readme
---
README.md | 198 ++++++++++++++++++++----------------------------------
1 file changed, 72 insertions(+), 126 deletions(-)
diff --git a/README.md b/README.md
index c0835eb..bf04586 100644
--- a/README.md
+++ b/README.md
@@ -1,138 +1,84 @@
# AgentPage
-Public profile builder for real estate agents. Agents get a branded page at `/:username` with listings, reviews, lead capture, and widgets. (Linktree alternative)
+Public profile builder for real estate agents. Each agent gets a branded page at `/:username` with listings, reviews, lead capture, and widgets — a Linktree alternative built for real estate.
-## Tech Stack
-
-| Layer | Technology |
-| -------------- | ------------------------------------------------------- |
-| Framework | Next.js 15 App Router |
-| Language | TypeScript |
-| Database | PostgreSQL + Prisma 7 (`prisma db push`, no migrations) |
-| Auth | Lucia v3 sessions + Google OAuth (Arctic) |
-| Payments | Stripe — plans `solo` / `pro` |
-| Storage | AWS S3 + CloudFront |
-| Rate Limiting | AWS DynamoDB |
-| Email | Resend |
-| i18n | next-intl (`en`, `fr`) |
-| UI dashboard | Tailwind CSS 4 + DaisyUI |
-| UI public page | Custom theme system — no DaisyUI |
-
-## Environment Variables
-
-```env
-APP_URL= # e.g. https://yourdomain.com
-DATABASE_URL=
-AWS_REGION=
-AWS_ACCESS_KEY_ID=
-AWS_SECRET_ACCESS_KEY=
-BUCKET_NAME= # S3 bucket for uploads
-CLOUDFRONT_URL= # Private (server-side) CDN URL
-NEXT_PUBLIC_CLOUDFRONT_URL= # Public CDN URL for
tags
-RATE_LIMITS_DYNAMODB_TABLE=
-RESEND_API_KEY=
-SENDER_EMAIL=
-GOOGLE_CLIENT_ID=
-GOOGLE_CLIENT_SECRET=
-NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
-STRIPE_SECRET_KEY=
-STRIPE_WEBHOOK_SECRET=
-STRIPE_PRICE_SOLO_MONTHLY= # Stripe Price ID
-STRIPE_PRICE_SOLO_ANNUAL=
-STRIPE_PRICE_PRO_MONTHLY=
-STRIPE_PRICE_PRO_ANNUAL=
-```
-
-## Setup
+## Getting Started
```bash
-bun install && cp .env.example .env.local # fill in env vars
-bunx prisma db push # sync schema to DB
+bun install
+bunx prisma db push
bun dev
```
-## Project Structure
-
-```
-src/
- app/
- [locale]/
- (dashboard)/ # Authenticated dashboard routes
- (public)/[username]/ # Public agent page
- api/
- auth/ # register, login, logout, google oauth
- dashboard/ # profile, links, reviews, listings, widgets, themes
- stripe/ # checkout, portal, webhook
- lib/
- auth.ts # getUser() → { user, session } | { user: null }
- db.ts # Prisma client singleton
- env.ts # Type-safe env via @t3-oss/env-nextjs
- subscription.ts # isPro(), getActivePlan(), hasActiveSubscription()
- themes.ts # getTheme() → ThemeConfig
- components/
- ui/pro-gate.tsx # Locks UI behind pro plan
- generated/prisma/ # Prisma client output
-locales/
- en.json # All translation keys
- fr.json
-```
-
-## Key Patterns
-
-### Auth
-
-`getUser()` from `@/lib/auth` returns `{ user, session }` or `{ user: null, session: null }`. Use in server components and API routes. Sessions stored in `Session` table; cookies set via Lucia.
-
-### Subscriptions & Pro Gating
-
-Plans: `solo` (paid base) and `pro` (paid advanced). Status values: `active`, `trialing`, `past_due`, `canceled`.
-
-```ts
-import { isPro } from "@/lib/subscription";
-const pro = await isPro(userId); // true if active/trialing pro subscription
-```
-
-Use `` wrapper for client components. Stripe customer ID is created eagerly at registration so the billing portal is always accessible.
-
-### Theme System (public page only)
-
-Dashboard uses DaisyUI. The public `/:username` page uses a custom theme — **never use DaisyUI classes here**.
-
-```ts
-import { getTheme } from "@/lib/themes";
-const theme = getTheme(
- user.theme,
- user.accentColor,
- user.buttonRadius,
- user.fontFamily,
- user.fontSize
-);
-// theme.bg, theme.cardBg, theme.cardText, theme.border, theme.muted → Tailwind classes
-// theme.accentHex, theme.accentTextColor → hex strings (use inline style)
-// theme.buttonRadius → Tailwind class (e.g. "rounded-lg")
-// theme.fontFamily, theme.fontSize → CSS string / Tailwind class
-```
-
-### i18n
-
-- Server components: `const t = await getTranslations("namespace")`
-- Client components: `const t = useTranslations("namespace")`
-- All keys live in `locales/en.json` and `locales/fr.json`
-- Namespaces: `common`, `pages.dashboard`, `pages.dashboard-listings`, `pages.agent-page`, etc.
-
-### Image Uploads
-
-Upload via `POST /api/dashboard/images` → returns CloudFront URL. Use `NEXT_PUBLIC_CLOUDFRONT_URL` in `` src.
-
-### Stripe Webhook
-
-Events at `POST /api/stripe/webhook`. On `checkout.session.completed`: cancels existing active/trialing subscriptions before creating the new one. Syncs status on `updated`, `deleted`, and `invoice.payment_*`.
-
-## Database Models
-
-`User` · `Session` · `Token` · `Subscription` · `Link` · `Review` · `Analytics` · `Lead` · `Listing`
+## Tech Stack
-Schema managed with `prisma db push` (no migrations). Client output in `src/generated/prisma`.
+| | |
+| ------------- | ------------------------------------------- |
+| **Framework** | Next.js 16 · React 19 · TypeScript 5.9 |
+| **Database** | PostgreSQL · Prisma 7 |
+| **Auth** | Lucia v3 · Google OAuth (Arctic) |
+| **Payments** | Stripe 19 |
+| **Storage** | AWS S3 · CloudFront |
+| **Email** | Resend |
+| **UI** | Tailwind CSS 4 · DaisyUI 5 |
+| **i18n** | next-intl 4 (EN / FR) |
+| **Infra** | Pulumi (IaC) · AWS DynamoDB (rate limiting) |
+
+## Features
+
+**Public agent page**
+
+- Customizable profile — photo, bio, brokerage, license, specialties
+- Links with click tracking and icons
+- Testimonials with star ratings
+- Property listings with photo, price, beds/baths
+- Widgets: mortgage calculator, home valuation, Calendly booking
+- Contact / lead capture form
+- Theme system: 8 base themes, 14 accent colors, font and radius controls
+
+**Dashboard**
+
+- Analytics: 7-day page views, link clicks, lead submissions
+- Lead inbox + CRM integrations (Zapier, Follow Up Boss, Mailchimp)
+- Billing portal: plan switching, invoice history
+- Multi-session management with browser/OS tracking
+- Live preview of public page
+
+## Subscriptions
+
+| Plan | Price |
+| -------- | ---------------------- |
+| **Solo** | $19/mo · $15/mo annual |
+| **Pro** | $39/mo · $31/mo annual |
+
+Pro unlocks: listings, widgets (mortgage calculator, home valuation, Calendly), and CRM integrations (Zapier, Follow Up Boss, Mailchimp). New accounts get a 14-day trial.
+
+Stripe handles checkout, billing portal, and webhooks. On new checkout, prior active subscriptions are automatically cancelled. Agents receive email notifications on payment failures and cancellation.
+
+## SEO
+
+- Per-agent dynamic OG metadata (name, bio, avatar)
+- Schema.org `RealEstateAgent` JSON-LD with `AggregateRating`
+- ISR on public pages (revalidate every 3600s)
+- Environment-aware `robots.txt` (blocks crawlers on dev/preview)
+- Vercel Analytics + Speed Insights
+
+## Technical Highlights
+
+- **Auth** — Lucia sessions (12-week expiry) with IP/browser/OS tracking. bcrypt for passwords. Google OAuth via Arctic.
+- **Rate limiting** — DynamoDB sliding-window rate limiter with per-endpoint limits (e.g. login: 5 req/3min, contact: 3 req/hr). Graceful degradation on DynamoDB outage.
+- **Image pipeline** — Presigned S3 uploads (5MB max, JPEG/PNG/WebP), served via CloudFront CDN. Old files deleted on replacement.
+- **CRM integrations** — Lead submissions fan out asynchronously post-response to Zapier, Follow Up Boss, and Mailchimp. Non-blocking — does not affect response time.
+- **Infrastructure as code** — AWS resources (S3, CloudFront, DynamoDB) provisioned with Pulumi. Separate dev/prod stacks.
+- **Theme system** — Fully composable: base theme + accent color + button radius + font family/size. Applied via Tailwind classes and inline hex values; dashboard (DaisyUI) and public page use separate styling layers.
+
+## Possible Improvements
+
+- Queue/retry mechanism for CRM webhook delivery (currently fire-and-forget)
+- Activate remaining i18n locales (FR, DE, ES, PT, IT — config exists, routing disabled)
+- Analytics event batching/sampling at scale
+- Email format validation before forwarding leads to Follow Up Boss
+- Complete Google Reviews auto-pull (referenced in upgrade modal, not yet implemented)
## License