Add rental system: listings, bookings, payments, payouts, reviews

Full rental marketplace with 6 categories (apartment, house, car, motorcycle, bicycle, ebike).
Booking workflow: create → confirm → pay → active → complete → payout.
Landlord dashboard, admin moderation, availability calendar, Stripe Connect payouts.
14 QA bugs found and fixed including validator schemas, API response types, HTTP methods.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
delta-lynx-89e8
2026-02-22 15:33:29 -08:00
parent 8961fa701a
commit dbbbbd26f4
90 changed files with 11052 additions and 124 deletions

View File

@@ -0,0 +1,39 @@
import { z } from 'zod';
export const createRentalSchema = z.object({
title: z.string().min(3).max(100),
description: z.string().min(10).max(5000),
category: z.enum(['APARTMENT', 'HOUSE', 'CAR', 'MOTORCYCLE', 'BICYCLE', 'EBIKE']),
location: z.string().min(1).max(200),
dailyPrice: z.number().positive().optional(),
monthlyPrice: z.number().positive().optional(),
depositAmount: z.number().min(0).optional(),
details: z.record(z.any()).optional(),
amenities: z.array(z.string()).optional(),
rules: z.array(z.string()).optional(),
cancellationPolicy: z.enum(['FLEXIBLE', 'MODERATE', 'STRICT']).optional(),
minDays: z.number().int().positive().optional(),
maxDays: z.number().int().positive().optional(),
minMonths: z.number().int().positive().optional(),
maxMonths: z.number().int().positive().optional(),
}).refine(data => data.dailyPrice || data.monthlyPrice, {
message: 'At least one price (daily or monthly) is required',
});
export const updateRentalSchema = z.object({
title: z.string().min(3).max(100).optional(),
description: z.string().min(10).max(5000).optional(),
category: z.enum(['APARTMENT', 'HOUSE', 'CAR', 'MOTORCYCLE', 'BICYCLE', 'EBIKE']).optional(),
location: z.string().min(1).max(200).optional(),
dailyPrice: z.number().positive().nullable().optional(),
monthlyPrice: z.number().positive().nullable().optional(),
depositAmount: z.number().min(0).nullable().optional(),
details: z.record(z.any()).optional(),
amenities: z.array(z.string()).optional(),
rules: z.array(z.string()).optional(),
cancellationPolicy: z.enum(['FLEXIBLE', 'MODERATE', 'STRICT']).optional(),
minDays: z.number().int().positive().nullable().optional(),
maxDays: z.number().int().positive().nullable().optional(),
minMonths: z.number().int().positive().nullable().optional(),
maxMonths: z.number().int().positive().nullable().optional(),
});