add crm auth, email, status update and search
All checks were successful
Auto Deploy / deploy (push) Successful in 1m7s

This commit is contained in:
deonisii
2026-04-17 21:29:14 +03:00
parent 246fb6d52d
commit 4f67bca4be
16 changed files with 502 additions and 35 deletions

View File

@@ -2,18 +2,49 @@
import { useState } from "react";
function normalizePhone(input: string) {
const digits = input.replace(/\D/g, "");
if (digits.length === 11 && (digits.startsWith("7") || digits.startsWith("8"))) {
return `7${digits.slice(1)}`;
}
if (digits.length === 10) {
return `7${digits}`;
}
return null;
}
function isValidEmail(email: string) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
export default function LeadForm() {
const [company, setCompany] = useState("");
const [phone, setPhone] = useState("");
const [email, setEmail] = useState("");
const [message, setMessage] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);
const [resultMessage, setResultMessage] = useState("");
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setIsSubmitting(true);
setResultMessage("");
const normalizedPhone = normalizePhone(phone);
if (!normalizedPhone) {
setResultMessage("Введите корректный российский телефон");
return;
}
if (!isValidEmail(email.trim())) {
setResultMessage("Введите корректный email");
return;
}
setIsSubmitting(true);
try {
const response = await fetch("/api/leads", {
method: "POST",
@@ -22,7 +53,8 @@ export default function LeadForm() {
},
body: JSON.stringify({
company,
phone,
phone: normalizedPhone,
email,
message,
}),
});
@@ -37,10 +69,10 @@ export default function LeadForm() {
setResultMessage("Заявка отправлена. Мы свяжемся с вами.");
setCompany("");
setPhone("");
setEmail("");
setMessage("");
} catch (error) {
console.error(error);
setResultMessage("Ошибка сети. Попробуйте ещё раз.");
} catch {
setResultMessage("Не удалось сохранить заявку");
} finally {
setIsSubmitting(false);
}
@@ -59,15 +91,24 @@ export default function LeadForm() {
<input
type="tel"
placeholder="+7 (___) ___-__-__"
placeholder="+7 (999) 123-45-67"
value={phone}
onChange={(e) => setPhone(e.target.value)}
className="w-full rounded-2xl border border-white/10 bg-black/30 px-5 py-4 outline-none placeholder:text-neutral-500 focus:border-emerald-500"
required
/>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full rounded-2xl border border-white/10 bg-black/30 px-5 py-4 outline-none placeholder:text-neutral-500 focus:border-emerald-500"
required
/>
<textarea
placeholder="Опишите текущий шлагбаум и что хотите добавить: номерной доступ, приложение, история, аналитика"
placeholder="Опишите текущий шлагбаум и что хотите добавить"
value={message}
onChange={(e) => setMessage(e.target.value)}
className="min-h-32 w-full rounded-2xl border border-white/10 bg-black/30 px-5 py-4 outline-none placeholder:text-neutral-500 focus:border-emerald-500"

View File

@@ -0,0 +1,62 @@
"use client";
import { useState } from "react";
const statuses = [
{ value: "NEW", label: "NEW" },
{ value: "IN_PROGRESS", label: "IN_PROGRESS" },
{ value: "CALL_SCHEDULED", label: "CALL_SCHEDULED" },
{ value: "WON", label: "WON" },
{ value: "LOST", label: "LOST" },
] as const;
export default function LeadStatusSelect({
leadId,
value,
}: {
leadId: string;
value: string;
}) {
const [status, setStatus] = useState(value);
const [isSaving, setIsSaving] = useState(false);
async function updateStatus(nextStatus: string) {
setStatus(nextStatus);
setIsSaving(true);
try {
const response = await fetch(`/api/leads/${leadId}`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ status: nextStatus }),
});
if (!response.ok) {
setStatus(value);
alert("Не удалось обновить статус");
}
} catch {
setStatus(value);
alert("Ошибка сети");
} finally {
setIsSaving(false);
}
}
return (
<select
value={status}
disabled={isSaving}
onChange={(e) => updateStatus(e.target.value)}
className="rounded-xl border border-white/10 bg-black/30 px-3 py-2 text-sm outline-none"
>
{statuses.map((item) => (
<option key={item.value} value={item.value}>
{item.label}
</option>
))}
</select>
);
}