315 lines
10 KiB
TypeScript
315 lines
10 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
export interface UserData {
|
|
id?: string;
|
|
firstname: string;
|
|
lastname: string;
|
|
email: string;
|
|
phoneNumber?: string;
|
|
description?: string;
|
|
companyName?: string;
|
|
companyOverview?: string;
|
|
companyWebsite?: string;
|
|
companyLocation?: string;
|
|
offeredServices?: string;
|
|
areasServed?: string[];
|
|
hasProfile?: boolean;
|
|
hasCompanyLogo?: boolean;
|
|
licensedIn?: string[];
|
|
gender?: 'male' | 'female';
|
|
customerType?: 'buyer' | 'seller' | 'professional';
|
|
customerSubType?: 'broker' | 'cpa' | 'attorney' | 'titleCompany' | 'surveyor' | 'appraiser';
|
|
created?: Date;
|
|
updated?: Date;
|
|
}
|
|
export type SortByOptions = 'priceAsc' | 'priceDesc' | 'creationDateFirst' | 'creationDateLast' | 'nameAsc' | 'nameDesc' | 'srAsc' | 'srDesc' | 'cfAsc' | 'cfDesc';
|
|
export type SortByTypes = 'professional' | 'listing' | 'business' | 'commercial';
|
|
export type Gender = 'male' | 'female';
|
|
export type CustomerType = 'buyer' | 'seller' | 'professional';
|
|
export type CustomerSubType = 'broker' | 'cpa' | 'attorney' | 'titleCompany' | 'surveyor' | 'appraiser';
|
|
export type ListingsCategory = 'commercialProperty' | 'business';
|
|
|
|
export const GenderEnum = z.enum(['male', 'female']);
|
|
export const CustomerTypeEnum = z.enum(['buyer', 'seller', 'professional']);
|
|
export const SubscriptionTypeEnum = z.enum(['free', 'professional', 'broker']);
|
|
export const CustomerSubTypeEnum = z.enum(['broker', 'cpa', 'attorney', 'titleCompany', 'surveyor', 'appraiser']);
|
|
export const ListingsCategoryEnum = z.enum(['commercialProperty', 'business']);
|
|
const PropertyTypeEnum = z.enum(['retail', 'land', 'industrial', 'office', 'mixedUse', 'multifamily', 'uncategorized']);
|
|
const TypeEnum = z.enum([
|
|
'automotive',
|
|
'industrialServices',
|
|
'foodAndRestaurant',
|
|
'realEstate',
|
|
'retail',
|
|
'oilfield',
|
|
'service',
|
|
'advertising',
|
|
'agriculture',
|
|
'franchise',
|
|
'professional',
|
|
'manufacturing',
|
|
'uncategorized',
|
|
]);
|
|
|
|
const USStates = z.enum([
|
|
'AL',
|
|
'AK',
|
|
'AZ',
|
|
'AR',
|
|
'CA',
|
|
'CO',
|
|
'CT',
|
|
'DC',
|
|
'DE',
|
|
'FL',
|
|
'GA',
|
|
'HI',
|
|
'ID',
|
|
'IL',
|
|
'IN',
|
|
'IA',
|
|
'KS',
|
|
'KY',
|
|
'LA',
|
|
'ME',
|
|
'MD',
|
|
'MA',
|
|
'MI',
|
|
'MN',
|
|
'MS',
|
|
'MO',
|
|
'MT',
|
|
'NE',
|
|
'NV',
|
|
'NH',
|
|
'NJ',
|
|
'NM',
|
|
'NY',
|
|
'NC',
|
|
'ND',
|
|
'OH',
|
|
'OK',
|
|
'OR',
|
|
'PA',
|
|
'RI',
|
|
'SC',
|
|
'SD',
|
|
'TN',
|
|
'TX',
|
|
'UT',
|
|
'VT',
|
|
'VA',
|
|
'WA',
|
|
'WV',
|
|
'WI',
|
|
'WY',
|
|
]);
|
|
export const AreasServedSchema = z.object({
|
|
county: z.string().optional().nullable(),
|
|
state: z
|
|
.string()
|
|
.nullable()
|
|
.refine(val => val !== null && val !== '', {
|
|
message: 'State is required',
|
|
}),
|
|
});
|
|
|
|
export const LicensedInSchema = z.object({
|
|
state: z
|
|
.string()
|
|
.nullable()
|
|
.refine(val => val !== null && val !== '', {
|
|
message: 'State is required',
|
|
}),
|
|
registerNo: z.string().nonempty('License number is required'),
|
|
});
|
|
export const GeoSchema = z.object({
|
|
name: z.string(),
|
|
state: z.string().refine(val => USStates.safeParse(val).success, {
|
|
message: 'Invalid state. Must be a valid 2-letter US state code.',
|
|
}),
|
|
latitude: z.number().refine(
|
|
value => {
|
|
return value >= -90 && value <= 90;
|
|
},
|
|
{
|
|
message: 'Latitude muss zwischen -90 und 90 liegen',
|
|
},
|
|
),
|
|
longitude: z.number().refine(
|
|
value => {
|
|
return value >= -180 && value <= 180;
|
|
},
|
|
{
|
|
message: 'Longitude muss zwischen -180 und 180 liegen',
|
|
},
|
|
),
|
|
});
|
|
const phoneRegex = /^(\+1|1)?[-.\s]?\(?[2-9]\d{2}\)?[-.\s]?\d{3}[-.\s]?\d{4}$/;
|
|
export const UserSchema = z
|
|
.object({
|
|
id: z.string().uuid().optional().nullable(),
|
|
firstname: z.string().min(2, { message: 'First name must contain at least 2 characters' }),
|
|
lastname: z.string().min(2, { message: 'Last name must contain at least 2 characters' }),
|
|
email: z.string().email({ message: 'Invalid email address' }),
|
|
phoneNumber: z.string().optional().nullable(),
|
|
description: z.string().optional().nullable(),
|
|
companyName: z.string().optional().nullable(),
|
|
companyOverview: z.string().optional().nullable(),
|
|
companyWebsite: z.string().url({ message: 'Invalid URL format' }).optional().nullable(),
|
|
companyLocation: GeoSchema.optional().nullable(),
|
|
offeredServices: z.string().optional().nullable(),
|
|
areasServed: z.array(AreasServedSchema).optional().nullable(),
|
|
hasProfile: z.boolean().optional().nullable(),
|
|
hasCompanyLogo: z.boolean().optional().nullable(),
|
|
licensedIn: z.array(LicensedInSchema).optional().nullable(),
|
|
gender: GenderEnum.optional().nullable(),
|
|
customerType: CustomerTypeEnum,
|
|
customerSubType: CustomerSubTypeEnum.optional().nullable(),
|
|
created: z.date().optional().nullable(),
|
|
updated: z.date().optional().nullable(),
|
|
subscriptionId: z.string().optional().nullable(),
|
|
subscriptionPlan: SubscriptionTypeEnum.optional().nullable(),
|
|
})
|
|
.superRefine((data, ctx) => {
|
|
if (data.customerType === 'professional') {
|
|
if (!data.customerSubType) {
|
|
ctx.addIssue({
|
|
code: z.ZodIssueCode.custom,
|
|
message: 'Customer subtype is required for professional customers',
|
|
path: ['customerSubType'],
|
|
});
|
|
}
|
|
|
|
if (!data.phoneNumber || !phoneRegex.test(data.phoneNumber)) {
|
|
ctx.addIssue({
|
|
code: z.ZodIssueCode.custom,
|
|
message: 'Phone number is required and must be in US format (XXX) XXX-XXXX for professional customers',
|
|
path: ['phoneNumber'],
|
|
});
|
|
}
|
|
|
|
if (!data.companyOverview || data.companyOverview.length < 10) {
|
|
ctx.addIssue({
|
|
code: z.ZodIssueCode.custom,
|
|
message: 'Company overview must contain at least 10 characters for professional customers',
|
|
path: ['companyOverview'],
|
|
});
|
|
}
|
|
|
|
if (!data.description || data.description.length < 10) {
|
|
ctx.addIssue({
|
|
code: z.ZodIssueCode.custom,
|
|
message: 'Description must contain at least 10 characters for professional customers',
|
|
path: ['description'],
|
|
});
|
|
}
|
|
|
|
if (!data.offeredServices || data.offeredServices.length < 10) {
|
|
ctx.addIssue({
|
|
code: z.ZodIssueCode.custom,
|
|
message: 'Offered services must contain at least 10 characters for professional customers',
|
|
path: ['offeredServices'],
|
|
});
|
|
}
|
|
|
|
if (!data.companyLocation) {
|
|
ctx.addIssue({
|
|
code: z.ZodIssueCode.custom,
|
|
message: 'Company location is required for professional customers',
|
|
path: ['companyLocation'],
|
|
});
|
|
}
|
|
|
|
if (!data.areasServed || data.areasServed.length < 1) {
|
|
ctx.addIssue({
|
|
code: z.ZodIssueCode.custom,
|
|
message: 'At least one area served is required for professional customers',
|
|
path: ['areasServed'],
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
export type AreasServed = z.infer<typeof AreasServedSchema>;
|
|
export type LicensedIn = z.infer<typeof LicensedInSchema>;
|
|
export type User = z.infer<typeof UserSchema>;
|
|
|
|
export const BusinessListingSchema = z.object({
|
|
id: z.string().uuid().optional().nullable(),
|
|
email: z.string().email(),
|
|
type: z.string().refine(val => TypeEnum.safeParse(val).success, {
|
|
message: 'Invalid type. Must be one of: ' + TypeEnum.options.join(', '),
|
|
}),
|
|
title: z.string().min(10),
|
|
description: z.string().min(10),
|
|
location: GeoSchema,
|
|
price: z.number().positive().max(1000000000),
|
|
favoritesForUser: z.array(z.string()),
|
|
draft: z.boolean(),
|
|
listingsCategory: ListingsCategoryEnum,
|
|
realEstateIncluded: z.boolean().optional().nullable(),
|
|
leasedLocation: z.boolean().optional().nullable(),
|
|
franchiseResale: z.boolean().optional().nullable(),
|
|
salesRevenue: z.number().positive().max(100000000),
|
|
cashFlow: z.number().positive().max(100000000),
|
|
supportAndTraining: z.string().min(5),
|
|
employees: z.number().int().positive().max(100000).optional().nullable(),
|
|
established: z.number().int().min(1800).max(2030).optional().nullable(),
|
|
internalListingNumber: z.number().int().positive().optional().nullable(),
|
|
reasonForSale: z.string().min(5).optional().nullable(),
|
|
brokerLicencing: z.string().min(5).optional().nullable(),
|
|
internals: z.string().min(5).optional().nullable(),
|
|
imageName: z.string().optional().nullable(),
|
|
created: z.date(),
|
|
updated: z.date(),
|
|
});
|
|
export type BusinessListing = z.infer<typeof BusinessListingSchema>;
|
|
|
|
export const CommercialPropertyListingSchema = z
|
|
.object({
|
|
id: z.string().uuid().optional().nullable(),
|
|
serialId: z.number().int().positive().optional().nullable(),
|
|
email: z.string().email(),
|
|
type: z.string().refine(val => PropertyTypeEnum.safeParse(val).success, {
|
|
message: 'Invalid type. Must be one of: ' + PropertyTypeEnum.options.join(', '),
|
|
}),
|
|
title: z.string().min(10),
|
|
description: z.string().min(10),
|
|
location: GeoSchema,
|
|
price: z.number().positive().max(1000000000),
|
|
favoritesForUser: z.array(z.string()),
|
|
listingsCategory: ListingsCategoryEnum,
|
|
draft: z.boolean(),
|
|
imageOrder: z.array(z.string()),
|
|
imagePath: z.string().nullable().optional(),
|
|
created: z.date(),
|
|
updated: z.date(),
|
|
})
|
|
.strict();
|
|
|
|
export type CommercialPropertyListing = z.infer<typeof CommercialPropertyListingSchema>;
|
|
|
|
export const SenderSchema = z.object({
|
|
name: z.string().min(6, { message: 'Name must be at least 6 characters long' }),
|
|
email: z.string().email({ message: 'Invalid email address' }),
|
|
phoneNumber: z.string().regex(/^(\+1|1)?[-.\s]?\(?[2-9]\d{2}\)?[-.\s]?\d{3}[-.\s]?\d{4}$/, {
|
|
message: 'Invalid US phone number format',
|
|
}),
|
|
state: z.string().refine(val => USStates.safeParse(val).success, {
|
|
message: 'Invalid state. Must be a valid 2-letter US state code.',
|
|
}),
|
|
comments: z.string().min(10, { message: 'Comments must be at least 10 characters long' }),
|
|
});
|
|
export type Sender = z.infer<typeof SenderSchema>;
|
|
export const ShareByEMailSchema = z.object({
|
|
name: z.string().min(6, { message: 'Name must be at least 6 characters long' }),
|
|
recipientEmail: z.string().email({ message: 'Invalid email address' }),
|
|
email: z.string().email({ message: 'Invalid email address' }),
|
|
listingTitle: z.string().optional().nullable(),
|
|
url: z.string().url({ message: 'Invalid URL format' }).optional().nullable(),
|
|
id: z.string().optional().nullable(),
|
|
type: ListingsCategoryEnum,
|
|
});
|
|
export type ShareByEMail = z.infer<typeof ShareByEMailSchema>;
|