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:
39
server/src/validators/rental.ts
Normal file
39
server/src/validators/rental.ts
Normal 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(),
|
||||
});
|
||||
Reference in New Issue
Block a user