From fd7df631e1467545bad08d582756a21b8f8357a2 Mon Sep 17 00:00:00 2001 From: nurdotnet <278048682+nurdotnet@users.noreply.github.com> Date: Sun, 3 May 2026 02:52:58 +0500 Subject: [PATCH] =?UTF-8?q?feat(signup):=20=D1=82=D0=B5=D0=BB=D0=B5=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=20=D0=BE=D0=B1=D1=8F=D0=B7=D0=B0=D1=82=D0=B5=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20+=20=D0=A4=D0=9B=D0=9A=20=D0=9A=D0=B0=D0=B7?= =?UTF-8?q?=D0=B0=D1=85=D1=81=D1=82=D0=B0=D0=BD=D0=B0=20(77XXXXXXXXX)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Фронт: - SignupForm: убрал «(необязательно)» из лейбла, добавил required + autoComplete=tel. - validation.ts: validatePhone теперь возвращает ошибку при пустом значении и валидирует строго KZ-мобильный (^77\d{9}$); ведущая «8» нормализуется в «7». «79…» (РФ) отвергается. Бэк: - AuthSignupController: SignupInput.Phone теперь string (не nullable). Добавлен NormalizeKzPhone — единая нормализация на сервере, защита от обхода фронтового валидатора. На запись в Organization.Phone уходит каноничная форма «+7XXXXXXXXXX». --- .../Controllers/AuthSignupController.cs | 20 +++++++++++++++++-- .../src/components/SignupForm.tsx | 3 ++- src/food-market.public/src/lib/validation.ts | 11 ++++++---- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/food-market.api/Controllers/AuthSignupController.cs b/src/food-market.api/Controllers/AuthSignupController.cs index b9880e6..4a0f67e 100644 --- a/src/food-market.api/Controllers/AuthSignupController.cs +++ b/src/food-market.api/Controllers/AuthSignupController.cs @@ -28,9 +28,22 @@ public AuthSignupController(AppDbContext db, UserManager userMgr) _db = db; _userMgr = userMgr; } - public record SignupInput(string Email, string Password, string OrganizationName, string? Phone, string? Plan); + public record SignupInput(string Email, string Password, string OrganizationName, string Phone, string? Plan); public record SignupResult(Guid OrganizationId, string Email); + /// Нормализация и проверка телефона Казахстана. Принимаем любое + /// форматирование (пробелы, скобки, +, дефисы), оставляем только цифры, + /// ведущая «8» переписывается в «7». Валидно: ровно 11 цифр, начинается + /// с «77» — мобильный код KZ. «79…» (РФ) и прочие отвергаем. + private static string? NormalizeKzPhone(string? raw) + { + if (string.IsNullOrWhiteSpace(raw)) return null; + var digits = new string(raw.Where(char.IsDigit).ToArray()); + if (digits.Length == 11 && digits[0] == '8') digits = "7" + digits[1..]; + if (digits.Length != 11 || !digits.StartsWith("77")) return null; + return "+" + digits; + } + [HttpPost("signup")] public async Task> Signup([FromBody] SignupInput input, CancellationToken ct) { @@ -39,6 +52,9 @@ public async Task> Signup([FromBody] SignupInput inpu return BadRequest(new { error = "Email, пароль и название обязательны." }); if (input.Password.Length < 8) return BadRequest(new { error = "Пароль минимум 8 символов." }); + var normalizedPhone = NormalizeKzPhone(input.Phone); + if (normalizedPhone is null) + return BadRequest(new { error = "Введите корректный номер Казахстана. Пример: +7 700 123 45 67" }); var existing = await _userMgr.FindByEmailAsync(input.Email); if (existing is not null) @@ -63,7 +79,7 @@ public async Task> Signup([FromBody] SignupInput inpu Name = input.OrganizationName.Trim(), CountryCode = "KZ", DefaultCurrencyId = kzt?.Id, - Phone = string.IsNullOrWhiteSpace(input.Phone) ? null : input.Phone.Trim(), + Phone = normalizedPhone, Email = input.Email.Trim(), }; _db.Organizations.Add(org); diff --git a/src/food-market.public/src/components/SignupForm.tsx b/src/food-market.public/src/components/SignupForm.tsx index c7c6104..610c5e0 100644 --- a/src/food-market.public/src/components/SignupForm.tsx +++ b/src/food-market.public/src/components/SignupForm.tsx @@ -92,8 +92,9 @@ export default function SignupForm({ defaultPlan = 'start' }: Props) { setOrgName(e.target.value)} placeholder="Наименование организации" className={inputCls(!!fieldErrors.orgName)} /> - + setPhone(e.target.value)} + required autoComplete="tel" inputMode="tel" placeholder="+7 700 123 45 67" className={inputCls(!!fieldErrors.phone)} />
diff --git a/src/food-market.public/src/lib/validation.ts b/src/food-market.public/src/lib/validation.ts index c243a7d..25bd8d8 100644 --- a/src/food-market.public/src/lib/validation.ts +++ b/src/food-market.public/src/lib/validation.ts @@ -30,10 +30,13 @@ export function validatePassword(value: string): string | null { } export function validatePhone(value: string): string | null { - if (!value) return null // optional - // +7 700 123 45 67 в любом форматировании, либо 8… - const digits = value.replace(/\D/g, '') - if (!/^[78]\d{10}$/.test(digits)) return 'Введите корректный телефон. Пример: +7 700 123 45 67' + if (!value || value.trim() === '') return 'Телефон обязателен для заполнения' + // Казахстан: +7 7XX XXX XX XX (мобильные коды KZ начинаются с 77, + // 79 — это РФ, исключаем). Принимаем любое форматирование (пробелы, + // скобки, дефисы), нормализуем к 11 цифрам, ведущая «8» = «7». + let digits = value.replace(/\D/g, '') + if (digits.length === 11 && digits.startsWith('8')) digits = '7' + digits.slice(1) + if (!/^77\d{9}$/.test(digits)) return 'Введите корректный номер Казахстана. Пример: +7 700 123 45 67' return null }