Validate booking contacts: required FIO and email, RF +7 phone mask, and email domain.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@moniki/cms",
|
||||
"version": "1.0.8",
|
||||
"version": "1.0.9",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import configPromise from '@payload-config'
|
||||
import { getPayload } from 'payload'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { isValidEmailAddress, isValidFullName, isValidRuPhone, normalizeRuPhone } from '@/lib/contact-fields'
|
||||
|
||||
function asText(value: unknown) {
|
||||
return typeof value === "string" ? value.trim() : ""
|
||||
@@ -17,14 +18,14 @@ function asId(value: unknown) {
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const body = (await req.json().catch(() => ({}))) as Record<string, unknown>
|
||||
const name = asText(body.name)
|
||||
const name = asText(body.name).replace(/\s+/g, " ")
|
||||
const email = asText(body.email)
|
||||
const phone = asText(body.phone)
|
||||
const phone = normalizeRuPhone(asText(body.phone))
|
||||
const branch = asId(body.branch)
|
||||
const service = asId(body.service)
|
||||
const doctor = asId(body.doctor)
|
||||
|
||||
if (!name || !email || !phone || branch == null || (service == null && doctor == null)) {
|
||||
if (!isValidFullName(name) || !isValidEmailAddress(email) || !isValidRuPhone(phone) || branch == null || (service == null && doctor == null)) {
|
||||
return NextResponse.json({ error: "invalid" }, { status: 400 })
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { ADMIN_GROUP } from '../admin/groups'
|
||||
import { isValidEmailAddress, isValidFullName, isValidRuPhone, normalizeRuPhone } from '../lib/contact-fields'
|
||||
|
||||
export const Inquiries: CollectionConfig = {
|
||||
slug: 'inquiries',
|
||||
@@ -16,10 +17,45 @@ export const Inquiries: CollectionConfig = {
|
||||
update: ({ req: { user } }) => user?.role === 'admin',
|
||||
delete: ({ req: { user } }) => user?.role === 'admin',
|
||||
},
|
||||
hooks: {
|
||||
beforeValidate: [
|
||||
({ data }) => {
|
||||
if (!data) return data
|
||||
if (typeof data.name === 'string') data.name = data.name.trim().replace(/\s+/g, ' ')
|
||||
if (typeof data.email === 'string') data.email = data.email.trim()
|
||||
if (typeof data.phone === 'string') {
|
||||
const phone = normalizeRuPhone(data.phone)
|
||||
if (phone) data.phone = phone
|
||||
}
|
||||
return data
|
||||
},
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
{ name: 'name', type: 'text', required: true, label: 'ФИО' },
|
||||
{ name: 'email', type: 'email', required: true, label: 'E-mail' },
|
||||
{ name: 'phone', type: 'text', required: true, label: 'Телефон' },
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
label: 'ФИО',
|
||||
validate: (value: string | null | undefined) =>
|
||||
isValidFullName(String(value ?? '')) ? true : 'Укажите фамилию и имя буквами',
|
||||
},
|
||||
{
|
||||
name: 'email',
|
||||
type: 'email',
|
||||
required: true,
|
||||
label: 'E-mail',
|
||||
validate: (value: string | null | undefined) =>
|
||||
isValidEmailAddress(String(value ?? '')) ? true : 'Укажите e-mail в формате name@domain.ru',
|
||||
},
|
||||
{
|
||||
name: 'phone',
|
||||
type: 'text',
|
||||
required: true,
|
||||
label: 'Телефон',
|
||||
validate: (value: string | null | undefined) =>
|
||||
isValidRuPhone(String(value ?? '')) ? true : 'Укажите телефон в формате +7 и 10 цифр',
|
||||
},
|
||||
{ name: 'service', type: 'relationship', relationTo: 'services', label: 'Услуга' },
|
||||
{ name: 'doctor', type: 'relationship', relationTo: 'doctors', label: 'Врач' },
|
||||
{ name: 'branch', type: 'relationship', relationTo: 'branches', required: true, label: 'Филиал' },
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[A-Za-z0-9-]{2,}$/
|
||||
|
||||
export function isValidFullName(value: string) {
|
||||
const name = value.trim().replace(/\s+/g, " ")
|
||||
if (name.length < 3) return false
|
||||
const parts = name.split(" ").filter(Boolean)
|
||||
if (parts.length < 2) return false
|
||||
return /^[\p{L}][\p{L}\s'-]*$/u.test(name)
|
||||
}
|
||||
|
||||
export function isValidEmailAddress(value: string) {
|
||||
return EMAIL_RE.test(value.trim())
|
||||
}
|
||||
|
||||
export function normalizeRuPhone(value: string) {
|
||||
let digits = value.replace(/\D/g, "")
|
||||
if (digits.startsWith("8")) digits = `7${digits.slice(1)}`
|
||||
if (!digits.startsWith("7")) digits = `7${digits}`
|
||||
digits = digits.slice(0, 11)
|
||||
return digits.length === 11 ? `+${digits}` : ""
|
||||
}
|
||||
|
||||
export function isValidRuPhone(value: string) {
|
||||
return /^\+7\d{10}$/.test(normalizeRuPhone(value))
|
||||
}
|
||||
Reference in New Issue
Block a user