Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | 3x 3x 3x 3x 3x | import { z } from "zod";
import { internationalPhoneSchema } from "@interioring/utils/validation/phone";
import { customerNameSchema } from "@interioring/utils/validation/customer-name";
import { freeTextSchema } from "@interioring/utils/validation/free-text";
export const createLeadSchema = z.object({
// Shared schema rejects numeric-only / single-char / too-short. Marketplace
// inquiries already use the same validator — keeping CRM in sync here so the
// rule never drifts (audit found the gap during issue #563 fix). Layer a
// `.max(200)` on top to preserve the existing column-aware ceiling.
customerName: customerNameSchema.pipe(
z.string().max(200, "Customer name is too long"),
),
// CRM leads support multiple countries (the AddLeadModal in Portal exposes
// a country-code selector). Use the international validator here, not the
// Indian-only one.
phone: internationalPhoneSchema,
email: z.string().email("Please enter a valid email").optional().or(z.literal("")),
// Free-text location: must have ≥1 letter when present so "12345" no longer
// passes. Allows digits ("HSR Layout 5th Block") via requireLetter+digits.
location: freeTextSchema({
minLen: 2,
requireLetter: true,
maxLen: 200,
}),
leadSourceId: z.number().int().positive("Please select a lead source"),
projectType: z.string().max(100).optional().or(z.literal("")),
budgetRange: z.string().max(100).optional().or(z.literal("")),
requirement: z.string().max(2000, "Requirement notes are too long").optional().or(z.literal("")),
});
// Partial-update schema for PATCH /:proId/crm/leads/:leadId. Same field rules
// as create, but every field is optional. Issue #563 audit caught that the
// PATCH path bypassed validation entirely (raw `c.req.json()`), allowing a pro
// to update an existing lead's name/location to junk values via the lead-edit
// UI — defeating the create-time fix.
export const updateLeadSchema = z.object({
customerName: customerNameSchema
.pipe(z.string().max(200, "Customer name is too long"))
.optional(),
phone: internationalPhoneSchema.optional(),
email: z.string().email("Please enter a valid email").optional().or(z.literal("")),
location: freeTextSchema({
minLen: 2,
requireLetter: true,
maxLen: 200,
}),
projectType: z.string().max(100).optional().or(z.literal("")),
budgetRange: z.string().max(100).optional().or(z.literal("")),
requirement: z
.string()
.max(2000, "Requirement notes are too long")
.optional()
.or(z.literal("")),
});
export const changeStageSchema = z.object({
stageId: z.number().int().positive(),
orderValuePaise: z.number().int().min(0).optional(),
lossReason: z.string().max(500).optional().or(z.literal("")),
lossReasonCategory: z.string().max(100).optional().or(z.literal("")),
});
export const contactMethodSchema = z.object({
method: z.string().min(1, "Contact method is required"),
note: z.string().max(1000).optional().or(z.literal("")),
});
export const valueSchema = z.object({
valuePaise: z.number().int().min(0, "Value must be non-negative"),
});
export type CreateLeadInput = z.infer<typeof createLeadSchema>;
export type UpdateLeadInput = z.infer<typeof updateLeadSchema>;
|