PRD.md

RentNGo - Product Requirements Document

Last Updated: June 2, 2026


Product Overview

RentNGo is a car rental platform with VIP transfer services for the Romanian market. Built with Next.js 16, Convex, and Clerk v7. Supports Romanian (default) and English.

Core Features:

  • Vehicle browsing and reservations with tiered pricing
  • VIP transfer bookings with distance-based pricing
  • Seasonal pricing multipliers
  • Admin dashboard for managing all resources
  • Email notifications for bookings

Completed Tasks ✅

TaskDateNotes
Default Pickup/Drop TimesJan 18Both default to 10:00
Sequential Calendar FlowJan 18Return calendar opens after pickup selection
Transfer Pricing UpdateJan 19Tiered km pricing + class multipliers
Remove Unused ComponentsJan 19~2,500 lines removed
Deprecated Fields CleanupJan 19Removed class, pricePerDay, payments table
Transfer Vehicle Selection UXJan 19Sticky floating card for selection
Rename Vehicle Classes AdminJan 19"Ordering" → "Classes"
Component Directory RestructuringJan 20Feature-based organization
Email Components ConsolidationJan 20Single source in convex/emails/
Vehicle Slug URLsJan 20SEO-friendly /cars/[slug] routes
Vehicle Class Multiplier ManagementJan 20UI in class detail page
Transfer Email - Vehicle DetailsJan 20Full vehicle info in confirmation
Transfer Booking - T&C LinksJan 20Terms & Privacy links added
Transfer Vehicle Seats ConfigJan 20Separate transferSeats field
Admin Role AuthorizationJan 22Clerk publicMetadata role check in middleware
File Naming CleanupJan 2224 files renamed to kebab-case, typo fixed
Translation Files CleanupJan 22Removed unused namespaces, consolidated keys, ~18% reduction
Barrel Import OptimizationJan 22Added optimizePackageImports for lucide-react, date-fns in next.config.ts
Dynamic Import Admin DialogsJan 2210 admin dialogs now lazy-loaded with next/dynamic
Memoize Expensive ComponentsJan 22React.memo() for 6 components, useMemo() for 5 calculations
Reduce getImageUrl CallsJan 22Moved imageUrl into queries (getAllVehiclesWithClasses, getFeaturedVehicles)
SEO OverhaulMar 23Shared metadata helper, dynamic html lang, x-default hreflang, JSON-LD XSS escaping, server-rendered structured data, generateStaticParams for cars/blog, noindex for transactional pages, breadcrumb schema for car details
Major Dependency UpgradesMar 23Next.js 16.2, Clerk v7, Zod v4, Recharts v3, date-fns v4, Resend v6, next-mdx-remote v6, Vercel analytics/speed-insights v2. Removed unused HeroUI (-189 packages). 0 vulnerabilities.
React CompilerMar 23Enabled reactCompiler: true — auto-memoization for all components
Error BoundariesMar 23Added global-error.tsx, [locale]/error.tsx, admin/error.tsx
CSS content-visibilityMar 23Applied to vehicle and blog grid cards for faster initial paint
Migrate to proxy.tsMar 23Renamed middleware.ts → proxy.ts per Next.js 16 convention
View TransitionsMar 23Enabled cross-fade page transitions via React 19.2 View Transitions API
Replace next lintMar 23Lint script now uses ESLint CLI directly (next lint removed in Next.js 16)
Google Tag ManagerMay 19GTM via @next/third-parties in root layout (NEXT_PUBLIC_GTM_ID); direct Google Ads gtag removed — now managed inside GTM
Release AutomationJun 2release-please (Conventional Commits → release PR, tag, GitHub Release). Reconciled package.json to 2.4.0 to match CHANGELOG
CI Quality GateJun 2GitHub Actions runs tsc --noEmit on PRs to main/develop. Build verified via Vercel preview
Fix ESLint ConfigJun 2npm run lint was crashing (FlatCompat + ESLint 9.39); switched to Next 16 native flat-config exports — lint runs again
Fix Broken Image UploadsJul 11Direct-to-storage uploads via files.generateUploadUrl + per-file POST; narrow vehicles.addImages persist; deleted bytes-through-args actions; blob-URL leak fix (RNGO-11)
Dead Code CleanupJul 1116 files (~1,290 lines) deleted: vehicle-card fork orphans, lib email stack, dead hooks/libs, admin settings page; dead searchAvailableVehicles query, VehicleSearchFilterForm uncontrolled fallback; deps removed: @clerk/react, @googlemaps/google-maps-services-js, framer-motion (consolidated on motion) (RNGO-15)
Server-side Pricing EngineJul 11Pure lib/pricing module (SCDW base-tier×season per owner decision, tiers, seasons, location fees, extras, transfer formula); createReservation/createTransfer recompute+overwrite money fields; persisted pricePerDay/rentalDays/basePrice + coded additionalCharges; Vitest suite (51 tests) + CI test job (RNGO-13)
Frontend PerformanceJul 12Split root layouts ([locale] + admin) to drop headers() — all public routes now static/ISR (1h) with per-locale <html lang>; static-safe Convex fetch helper; mapbox-gl (~1.6MB) code-split out of transfer routes; hero via next/image fill+priority; card image priority for first 4 only; MDX toolchain out of public blog bundles; lazy testimonial avatars; mapbox suggest stale-guard; skip redundant Directions call on restore (RNGO-16)
Reservation Email FixesJul 12Included-km row (days×200) + extra-km-opted row in confirmation emails; RO SCDW/warranty labels now passed to pricing section (were falling back to EN); SCDW amount + day count come from the persisted server pricing breakdown (RNGO-24; explicit extra-km row activates when RNGO-17 sends structured extras)
AI-SEO SurfacesJul 12robots.ts explicit AI-crawler allows (retrieval + training), /llms.txt route from slim Convex fleet query, bilingual /faq page with FAQPage JSON-LD + sitemap/footer links, canonical lib/company.ts + shared AutoRental schema builder (FAQ copy is a draft pending client approval; identity-data migration is RNGO-20) (RNGO-21)
Reservation Page DecompositionJul 121888-line page → ~400-line composition of 6 memoized cards + useReservationForm + memoized useReservationPricing over lib/pricing (display/submit/persisted totals now one breakdown); structured extras sent to createReservation (activates coded charges + email extra-km row); shared checkout components reused by transfer booking (payment list now translated there); confirmation renders persisted breakdown w/ reservationCharges i18n catalog + legacy fallback; dead decomposition cluster deleted (RNGO-17)
Coupon CodesJul 12coupons + couponRedemptions tables (replaces dead promotions table + free-text promo plumbing); pure discount math in lib/pricing/discount.ts (24 tests); advisory validateCoupon query + atomic applyAndRedeemCoupon inside createReservation/createTransfer (OCC-safe max-redemptions, once-per-user by account/email); admin CRUD at /admin/coupons; coupon input on both checkouts; discount persisted + shown on confirmations and emails; single-discount seam ready for affiliate (RNGO-25)
Affiliate ProgramJul 12/r/<slug> referral links (locale-safe route handler + proxy skip); consent-gated first-party referral cookie (minimal self-built banner — no CMP existed); affiliates/affiliateSettings/referralAttributions/referralConversions tables; conversion confirmed on booking created, voided on cancel/delete (re-confirmed on un-cancel), OCC-safe counter; referred discount + count-derived tier reward via the pickDiscount seam (coupon wins, one discount per booking, all amounts admin-configurable w/ per-affiliate overrides); admin UI at /admin/affiliates (settings + tier editor + void); affiliate dashboard on profile; 24 new Vitest tests (RNGO-26)
Booking Flow Layout UniformityJul 12Layout-only: reservation coupon card into left column (columns balance, no stranded full-width row); car detail title lifted above the grid (level columns, title-first on mobile, removed double gap under rental details); transfers booking heading above grid + coupon into sticky summary sidebar; transfer search form passengers fills the empty right slot on one-way; transfers landing section paddings tightened
Dev Environment (develop → dev.rngo.ro)Jul 13Named Convex deployment develop (prod-type, same project) seeded from prod snapshot; Vercel Preview CONVEX_DEPLOY_KEY scoped to develop branch; dev.rngo.ro branch domain; Clerk dev instance + Resend wired; convex 1.42.1 + @convex-dev/resend 0.2.5
Dev Environment DifferentiationJul 13Non-production deployments noindexed (robots.ts disallow-all + layout robots meta via VERCEL_ENV); email sender/subject env-driven (EMAIL_FROM, EMAIL_SUBJECT_PREFIX → "Rent'n Go [DEV]"/"[DEV] " on develop); email logo origin via SITE_URL
Mise SetupJul 13mise.toml with node pinning, .env.local loading, and task runner replacing npm scripts

Planned Tasks — Convex Hardening

Tasks from a full audit of Convex functions against official guidelines and best practices.

P0 — Security & Correctness

TaskDescription
Auth guards on admin mutationsvehicles, seasons, blogs, vehicleClasses, transferPricing mutations are all public with zero auth — anyone can create/delete data. Add auth checks or convert to internalMutation
Stop accepting userId as argumentDone (RNGO-13) — both create mutations now derive the user from ctx auth; the arg is still accepted-and-ignored for client compatibility, drop it when the client stops sending it
Replace .filter() with .withIndex().filter() causes full table scans. Violations in vehicles.getAll, searchAvailableVehicles, getByClass, blogs.getAll, featuredCars.setFeaturedCar, vehicleClasses.remove. Add missing indexes (classId, transmission, fuelType, vehicleId on featuredCars)
Switch identity.subject → tokenIdentifierAll user lookups use identity.subject — guidelines say to prefer tokenIdentifier as the canonical stable identifier

P1 — Performance & Scalability

TaskDescription
Reservation/transfer number via counter docDone (RNGO-13) — full-table .collect() replaced with a by_number index read (.order("desc").first()) in both create mutations
Bound unbounded .collect() callsMultiple queries collect full tables with no limits — getAllVehicles, searchAvailableVehicles, getAllVehiclesWithClasses, getAllReservations, getAllTransfers, stats/chart queries. Add .take(n) or pagination
Standardize auth patterntransfers.ts does raw ctx.auth + manual user lookup everywhere. reservations.ts uses getCurrentUser helpers. Standardize on the helper pattern across all files

P2 — Code Quality

TaskDescription
Add returns validatorsMany functions missing returns — inconsistent with others that have them. Add across vehicles.ts, reservations.ts, transfers.ts, featuredCars.ts
Fix seasons.ts broken filterObject.entries(updates).filter(([value]) => ...) — destructuring bug, checks key instead of value. Should be ([_, value])
Remove as any castsreservations.ts:83 uses (r as any).reservationNumber, blogs.ts:286 uses any for updates object. Use proper types
Add lint to CI gateResolve the ~75 pre-existing npm run lint errors (mostly no-explicit-any, overlaps the as any / v.any() tasks above), then add a blocking npm run lint step to .github/workflows/ci.yml

In Progress

TaskDescription
Bilingual Blog ContentBlogs have separate RO/EN title, description, content, readingTime. Public queries accept locale, admin UI has RO/EN tabs. Migration: npx convex run migrations/bilingualBlogs

Planned Tasks — July 2026 Roadmap

Full plans in .claude/plans/ (one file per workstream); tackle order + client decisions needed in .claude/plans/00-priorities.md. Based on the 103-finding audit in AUDIT.md + client feature requests.

PriorityTaskPlan
P0Security: auth on 28 admin Convex writes, ownership checks, PII queries, email routeaudit-pricing-security.md
P1Convex perf: counter doc, indexes, pagination, statsaudit-convex-performance.md (absorbs "Convex Hardening" section above)
P2Admin dialog dedup; frontend perf; i18n extraction; misc bug batchesaudit-*.md
P3AI-SEO pages (robots/llms.txt/FAQ/local business)feature-ai-seo.md
P4Copy update (waiting on client copy) · Calendar changes (waiting on clarification)feature-copy-update.md, feature-calendar.md

Planned Tasks — Other

PriorityTaskDescription
P2Copy & Content UpdateReview translations for tone, grammar, consistency
P3Time Picker Dark Theme FixWhite-on-white text on Windows in dark mode
P3.2Lazy State InitializationUse function form for useState with expensive initial values
P3.3Functional setState UpdatesUse functional setState for stable callbacks and prevent stale closures
P4Turbopack MigrationRemove --webpack flag once @next/mdx supports rehype/remark plugins in Turbopack

Key Business Rules

Reservations:

  • Minimum 1 day rental
  • Same-day = 1 day; return 2+ hours late = extra day
  • Payment: cash, card on delivery, or card online

Transfers:

  • Distance via Mapbox; supports one-way and round-trip
  • Pricing: base fare (covers first 15km) + tiered per-km rate × class multiplier
  • Payment: cash or card on delivery only

Seasonal Pricing:

  • Season with most overlap applies
  • Default multiplier: 1.0

Revision History

VersionDateSummary
1.0Jan 18Initial PRD
2.0Jan 20Major features complete
3.0Jan 22Simplified to task-focused format
3.1Jan 22Admin role authorization via Clerk publicMetadata
3.2Jan 22File naming cleanup; added lint migration task
3.3Jan 22Translation files cleanup and consolidation
3.4Jan 22Barrel import optimization; added React perf subtasks
3.5Jan 22Dynamic import admin dialogs
3.6Jan 22Reduced getImageUrl calls - imageUrl now included in queries
4.0Mar 23Major dependency upgrades + post-upgrade improvements
4.1Mar 23Added Convex Hardening phase from full guidelines audit
4.2Jun 2Release automation (release-please) + CI typecheck gate + ESLint config fix