PRD.md

RentNGo - Product Requirements Document

Last Updated: March 23, 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)

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 argumentcreateReservation and createTransfer accept userId in args — must derive from ctx.auth.getUserIdentity() server-side per Convex guidelines
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 doccreateReservation and createTransfer .collect() the entire table to compute max + 1. Use a dedicated counter document instead
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])
Replace v.any() in blog uploadsblogs.uploadImages uses v.array(v.any()) — should be v.array(v.bytes())
Remove as any castsreservations.ts:83 uses (r as any).reservationNumber, blogs.ts:286 uses any for updates object. Use proper types

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 — 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