import { z } from 'zod'; // Base community listing schema export const communityListingSchema = z.object({ title: z .string() .min(1, { error: 'Title is required' }) .max(255, { error: 'Title must be less than 255 characters' }), description: z .string() .max(2000, { error: 'Description must be less than 2000 characters' }) .optional(), listing_type: z.enum(['product', 'service', 'tool', 'skill', 'need'], { errorMap: () => ({ error: 'Please select a valid listing type' }), }), category: z.string().min(1, { error: 'Category is required' }), subcategory: z.string().optional(), // Product/Tool specific fields condition: z.enum(['new', 'like_new', 'good', 'fair', 'needs_repair']).optional(), price: z.number().min(0, { error: 'Price must be non-negative' }).optional(), price_type: z.enum(['free', 'sale', 'rent', 'trade', 'borrow']).optional(), quantity: z.number().int().min(1, { error: 'Quantity must be at least 1' }).optional(), // Service/Skill specific fields service_type: z.string().optional(), rate: z.number().min(0, { error: 'Rate must be non-negative' }).optional(), rate_type: z.string().optional(), // Location latitude: z.number().min(-90).max(90).optional(), longitude: z.number().min(-180).max(180).optional(), // Availability pickup_available: z.boolean(), delivery_available: z.boolean(), delivery_radius_km: z.number().min(0).max(500).optional(), // Media images: z .array(z.string().url({ error: 'Invalid image URL' })) .max(10, { error: 'Maximum 10 images allowed' }) .optional(), // Metadata tags: z .array(z.string().max(50, { error: 'Tag must be less than 50 characters' })) .max(20, { error: 'Maximum 20 tags allowed' }) .optional(), }); // Conditional validation based on listing type export const communityListingFormSchema = communityListingSchema.superRefine((data, ctx) => { // If listing type is product/tool, require condition and price info if (data.listing_type === 'product' || data.listing_type === 'tool') { if (!data.condition) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Condition is required for products and tools', path: ['condition'], }); } if (data.price !== undefined && !data.price_type) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Price type is required when price is specified', path: ['price_type'], }); } if (data.price_type && data.price_type !== 'free' && data.price === undefined) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Price is required for this price type', path: ['price'], }); } } // If listing type is service/skill, require service info if (data.listing_type === 'service' || data.listing_type === 'skill') { if (!data.service_type) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Service type is required for services and skills', path: ['service_type'], }); } if (data.rate !== undefined && !data.rate_type) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Rate type is required when rate is specified', path: ['rate_type'], }); } } // Location validation - if one coordinate provided, both required if ((data.latitude && !data.longitude) || (!data.latitude && data.longitude)) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Both latitude and longitude are required for location', path: ['latitude'], }); ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Both latitude and longitude are required for location', path: ['longitude'], }); } // Delivery validation if (data.delivery_available && !data.delivery_radius_km) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Delivery radius is required when delivery is available', path: ['delivery_radius_km'], }); } if (data.delivery_radius_km && !data.delivery_available) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Delivery must be enabled to set delivery radius', path: ['delivery_available'], }); } }); // Type inference export type CommunityListingFormData = z.infer; // Categories by listing type export const LISTING_CATEGORIES = { product: [ 'Electronics', 'Furniture', 'Clothing', 'Books', 'Sports Equipment', 'Tools & Hardware', 'Home & Garden', 'Vehicles', 'Other', ], service: [ 'Home Repair', 'Cleaning', 'Tutoring', 'Transportation', 'Event Planning', 'Pet Care', 'Beauty & Wellness', 'Consulting', 'Other', ], tool: [ 'Power Tools', 'Hand Tools', 'Garden Tools', 'Kitchen Tools', 'Measuring Tools', 'Safety Equipment', 'Other', ], skill: ['Teaching', 'Programming', 'Design', 'Music', 'Sports', 'Languages', 'Crafts', 'Other'], need: [ 'Looking for Tools', 'Looking for Services', 'Looking for Items', 'Looking for Skills', 'Looking for Transportation', 'Other', ], } as const; export const CONDITION_OPTIONS = [ { value: 'new', label: 'New' }, { value: 'like_new', label: 'Like New' }, { value: 'good', label: 'Good' }, { value: 'fair', label: 'Fair' }, { value: 'needs_repair', label: 'Needs Repair' }, ] as const; export const PRICE_TYPE_OPTIONS = [ { value: 'free', label: 'Free' }, { value: 'sale', label: 'For Sale' }, { value: 'rent', label: 'For Rent' }, { value: 'trade', label: 'For Trade' }, { value: 'borrow', label: 'For Borrow' }, ] as const; export const SERVICE_TYPE_OPTIONS = [ { value: 'offering', label: 'Offering Service' }, { value: 'seeking', label: 'Seeking Service' }, ] as const; export const RATE_TYPE_OPTIONS = [ { value: 'hourly', label: 'Per Hour' }, { value: 'fixed', label: 'Fixed Price' }, { value: 'negotiable', label: 'Negotiable' }, { value: 'free', label: 'Free' }, { value: 'trade', label: 'For Trade' }, ] as const;