mirror of
https://git.meshkee.com/Meshkee/dashboards.git
synced 2026-08-11 22:30:58 +04:30
Show existing-Meshkee signup notice as an acknowledge modal.
After admit, continue via SMS OTP so login-otp can link the user to that website with unchanged password/profile. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
cce28a4d7c
commit
8d59166fe8
@@ -328,10 +328,14 @@ const en = {
|
|||||||
'signup.error.length': 'Password must be at least 8 characters.',
|
'signup.error.length': 'Password must be at least 8 characters.',
|
||||||
'signup.error.create': 'Unable to create account.',
|
'signup.error.create': 'Unable to create account.',
|
||||||
'signup.error.cellExists':
|
'signup.error.cellExists':
|
||||||
'This mobile number already has a Meshkee account on {sites}. For a better experience we use that account’s name and password. Sign in with that password, reset it, or use one-time login — then you can access this website too.',
|
'This mobile number already has an account in Meshkee Unified Management System (__MESHKEE_APP__) on {sites}. Your password and data will stay the same. Continue to verify by SMS and join this website too.',
|
||||||
'signup.error.cellExistsGeneric':
|
'signup.error.cellExistsGeneric':
|
||||||
'This mobile number already has a Meshkee account on another website. For a better experience we use that account’s name and password. Sign in with that password, reset it, or use one-time login — then you can access this website too.',
|
'This mobile number already has an account in Meshkee Unified Management System (__MESHKEE_APP__) on another website. Your password and data will stay the same. Continue to verify by SMS and join this website too.',
|
||||||
'signup.info.verify': 'Account created. Enter the SMS code to verify your number and sign in.',
|
'signup.info.verify': 'Account created. Enter the SMS code to verify your number and sign in.',
|
||||||
|
'signup.info.verifyExisting':
|
||||||
|
'Enter the SMS code to join this website. Your Meshkee password and profile stay the same.',
|
||||||
|
'signup.cellExists.title': 'Existing Meshkee account',
|
||||||
|
'signup.cellExists.admit': 'I understand, continue',
|
||||||
|
|
||||||
'forgot.back': 'Back to sign in',
|
'forgot.back': 'Back to sign in',
|
||||||
'forgot.title': 'Forgot password',
|
'forgot.title': 'Forgot password',
|
||||||
@@ -709,10 +713,14 @@ const fa: Record<MessageKey, string> = {
|
|||||||
'signup.error.length': 'رمز عبور باید حداقل ۸ کاراکتر باشد.',
|
'signup.error.length': 'رمز عبور باید حداقل ۸ کاراکتر باشد.',
|
||||||
'signup.error.create': 'ایجاد حساب ممکن نشد.',
|
'signup.error.create': 'ایجاد حساب ممکن نشد.',
|
||||||
'signup.error.cellExists':
|
'signup.error.cellExists':
|
||||||
'این شماره موبایل قبلاً در مشکی حساب دارد ({sites}). برای تجربه بهتر از همان نام و رمز استفاده میکنیم. با همان رمز وارد شوید، رمز را بازیابی کنید، یا ورود یکبارمصرف را امتحان کنید — بعد میتوانید وارد این وبسایت هم شوید.',
|
'این شماره موبایل قبلاً در سیستم مدیریت یکپارچه ی مشکی (__MESHKEE_APP__) حساب دارد ({sites}). رمز عبور و اطلاعات شما همان خواهد بود. با ادامه، تأیید پیامکی را انجام دهید تا وارد این وبسایت هم شوید.',
|
||||||
'signup.error.cellExistsGeneric':
|
'signup.error.cellExistsGeneric':
|
||||||
'این شماره موبایل قبلاً در مشکی (وبسایت دیگری) حساب دارد. برای تجربه بهتر از همان نام و رمز استفاده میکنیم. با همان رمز وارد شوید، رمز را بازیابی کنید، یا ورود یکبارمصرف را امتحان کنید — بعد میتوانید وارد این وبسایت هم شوید.',
|
'این شماره موبایل قبلاً در سیستم مدیریت یکپارچه ی مشکی (__MESHKEE_APP__) (وبسایت دیگری) حساب دارد. رمز عبور و اطلاعات شما همان خواهد بود. با ادامه، تأیید پیامکی را انجام دهید تا وارد این وبسایت هم شوید.',
|
||||||
'signup.info.verify': 'حساب ایجاد شد. کد پیامکی را وارد کنید تا شماره تأیید شود و وارد شوید.',
|
'signup.info.verify': 'حساب ایجاد شد. کد پیامکی را وارد کنید تا شماره تأیید شود و وارد شوید.',
|
||||||
|
'signup.info.verifyExisting':
|
||||||
|
'کد پیامکی را وارد کنید تا به این وبسایت بپیوندید. رمز عبور و اطلاعات مشکی شما همان میماند.',
|
||||||
|
'signup.cellExists.title': 'حساب موجود در مشکی',
|
||||||
|
'signup.cellExists.admit': 'متوجه شدم، ادامه',
|
||||||
|
|
||||||
'forgot.back': 'بازگشت به ورود',
|
'forgot.back': 'بازگشت به ورود',
|
||||||
'forgot.title': 'فراموشی رمز عبور',
|
'forgot.title': 'فراموشی رمز عبور',
|
||||||
|
|||||||
@@ -301,13 +301,20 @@ p.footerText {
|
|||||||
|
|
||||||
.error {
|
.error {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #b91c1c;
|
line-height: 1.55;
|
||||||
background: rgba(239, 68, 68, 0.08);
|
color: #fecaca;
|
||||||
border: 1px solid rgba(239, 68, 68, 0.25);
|
background: rgba(239, 68, 68, 0.16);
|
||||||
|
border: 1px solid rgba(248, 113, 113, 0.45);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global(html:not([data-theme='dark'])) .error {
|
||||||
|
color: #b91c1c;
|
||||||
|
background: rgba(239, 68, 68, 0.08);
|
||||||
|
border-color: rgba(239, 68, 68, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
@@ -317,6 +324,101 @@ p.footerText {
|
|||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.noticeOverlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 300;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20px;
|
||||||
|
background: var(--modal-overlay-bg, rgba(0, 0, 0, 0.6));
|
||||||
|
backdrop-filter: blur(6px);
|
||||||
|
-webkit-backdrop-filter: blur(6px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.noticeModal {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 420px;
|
||||||
|
padding: 22px 22px 20px;
|
||||||
|
background: var(--elevated-surface, var(--surface));
|
||||||
|
border: 1px solid var(--glass-border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
box-shadow: var(--glass-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.noticeHeader {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noticeTitle {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--text-primary);
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noticeClose {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
transition: background 0.2s, color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noticeClose:hover {
|
||||||
|
background: rgba(148, 163, 184, 0.15);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.noticeBody {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.75;
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noticeLink {
|
||||||
|
color: var(--primary);
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noticeLink:hover {
|
||||||
|
color: var(--primary-dark, var(--primary));
|
||||||
|
}
|
||||||
|
|
||||||
|
.noticeAdmit {
|
||||||
|
width: 100%;
|
||||||
|
min-height: var(--field-height);
|
||||||
|
padding: 11px 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #fff;
|
||||||
|
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
box-shadow: 0 4px 12px rgba(var(--primary-rgb) / 0.3);
|
||||||
|
transition: transform 0.2s, opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noticeAdmit:hover:not(:disabled) {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.noticeAdmit:disabled {
|
||||||
|
opacity: 0.65;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
.domainHint {
|
.domainHint {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState } from 'react'
|
import { useState, type ReactNode } from 'react'
|
||||||
|
import { createPortal } from 'react-dom'
|
||||||
import { useNavigate, useSearchParams } from 'react-router-dom'
|
import { useNavigate, useSearchParams } from 'react-router-dom'
|
||||||
import { Eye, EyeOff, Smartphone, Lock, KeyRound, ArrowLeft, User } from 'lucide-react'
|
import { Eye, EyeOff, Smartphone, Lock, KeyRound, ArrowLeft, User, X } from 'lucide-react'
|
||||||
import {
|
import {
|
||||||
useAuth,
|
useAuth,
|
||||||
CUSTOMER_ACCESS_MESSAGE,
|
CUSTOMER_ACCESS_MESSAGE,
|
||||||
@@ -15,7 +16,7 @@ import {
|
|||||||
resetPassword,
|
resetPassword,
|
||||||
sendOtp,
|
sendOtp,
|
||||||
} from '../services/authService'
|
} from '../services/authService'
|
||||||
import { LanguageSelect, PasswordStrengthMeter, isPasswordStrong } from '@meshkee/dashboard-ui'
|
import { LanguageSelect, PasswordStrengthMeter, isPasswordStrong, useLocale } from '@meshkee/dashboard-ui'
|
||||||
import { useT } from '../i18n/useT'
|
import { useT } from '../i18n/useT'
|
||||||
import { safeRedirectPath } from '../lib/redirect'
|
import { safeRedirectPath } from '../lib/redirect'
|
||||||
import meshkeeLogo from '../assets/meshkee-logo.png'
|
import meshkeeLogo from '../assets/meshkee-logo.png'
|
||||||
@@ -24,6 +25,38 @@ import styles from './LoginPage.module.css'
|
|||||||
type AuthView = 'login' | 'signup' | 'forgot' | 'otp'
|
type AuthView = 'login' | 'signup' | 'forgot' | 'otp'
|
||||||
type SmsStep = 'phone' | 'code'
|
type SmsStep = 'phone' | 'code'
|
||||||
|
|
||||||
|
const MESHKEE_APP_MARKER = '__MESHKEE_APP__'
|
||||||
|
const MESHKEE_SITE_URL = 'https://meshkee.com'
|
||||||
|
|
||||||
|
function parseCellExistsSites(message: string): string | null {
|
||||||
|
if (message.startsWith('CELL_EXISTS_OTHER_SITE:')) {
|
||||||
|
return message.slice('CELL_EXISTS_OTHER_SITE:'.length).trim()
|
||||||
|
}
|
||||||
|
if (message.includes('Cell number exists on another account')) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderWithMeshkeeLink(message: string, linkClassName: string): ReactNode {
|
||||||
|
const idx = message.indexOf(MESHKEE_APP_MARKER)
|
||||||
|
if (idx === -1) return message
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{message.slice(0, idx)}
|
||||||
|
<a
|
||||||
|
href={MESHKEE_SITE_URL}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className={linkClassName}
|
||||||
|
>
|
||||||
|
Meshkee.app
|
||||||
|
</a>
|
||||||
|
{message.slice(idx + MESHKEE_APP_MARKER.length)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function LoginPage() {
|
export function LoginPage() {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const [searchParams] = useSearchParams()
|
const [searchParams] = useSearchParams()
|
||||||
@@ -32,6 +65,7 @@ export function LoginPage() {
|
|||||||
const { businessName, businessNameEn, logoUrl } = useTenantBranding()
|
const { businessName, businessNameEn, logoUrl } = useTenantBranding()
|
||||||
const tenantDomain = getTenantDomain()
|
const tenantDomain = getTenantDomain()
|
||||||
const t = useT()
|
const t = useT()
|
||||||
|
const { locale, dir } = useLocale()
|
||||||
|
|
||||||
const [view, setView] = useState<AuthView>('login')
|
const [view, setView] = useState<AuthView>('login')
|
||||||
const [smsStep, setSmsStep] = useState<SmsStep>('phone')
|
const [smsStep, setSmsStep] = useState<SmsStep>('phone')
|
||||||
@@ -49,6 +83,8 @@ export function LoginPage() {
|
|||||||
const [error, setError] = useState('')
|
const [error, setError] = useState('')
|
||||||
const [info, setInfo] = useState('')
|
const [info, setInfo] = useState('')
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||||
|
/** `null` closed; `''` generic notice; otherwise site names */
|
||||||
|
const [cellExistsSites, setCellExistsSites] = useState<string | null>(null)
|
||||||
|
|
||||||
function clearMessages() {
|
function clearMessages() {
|
||||||
setError('')
|
setError('')
|
||||||
@@ -67,6 +103,7 @@ export function LoginPage() {
|
|||||||
setSmsStep('phone')
|
setSmsStep('phone')
|
||||||
setCodeSent(false)
|
setCodeSent(false)
|
||||||
setShowPassword(false)
|
setShowPassword(false)
|
||||||
|
setCellExistsSites(null)
|
||||||
clearMessages()
|
clearMessages()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,18 +139,6 @@ export function LoginPage() {
|
|||||||
err.message.includes('Unable to send SMS')
|
err.message.includes('Unable to send SMS')
|
||||||
) {
|
) {
|
||||||
setError(t('login.error.smsProvider'))
|
setError(t('login.error.smsProvider'))
|
||||||
} else if (
|
|
||||||
err.message.includes('CELL_EXISTS_OTHER_SITE:') ||
|
|
||||||
err.message.includes('Cell number exists on another account')
|
|
||||||
) {
|
|
||||||
const sites = err.message.startsWith('CELL_EXISTS_OTHER_SITE:')
|
|
||||||
? err.message.slice('CELL_EXISTS_OTHER_SITE:'.length).trim()
|
|
||||||
: ''
|
|
||||||
setError(
|
|
||||||
sites
|
|
||||||
? t('signup.error.cellExists', { sites })
|
|
||||||
: t('signup.error.cellExistsGeneric'),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
setError(err.message)
|
setError(err.message)
|
||||||
}
|
}
|
||||||
@@ -177,20 +202,7 @@ export function LoginPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSignup(e: React.FormEvent) {
|
async function runSignup() {
|
||||||
e.preventDefault()
|
|
||||||
clearMessages()
|
|
||||||
|
|
||||||
if (password !== confirmPassword) {
|
|
||||||
setError(t('signup.error.match'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isPasswordStrong(password)) {
|
|
||||||
setError(t('forgot.error.strength'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const cellNumber = resolveCellNumber()
|
const cellNumber = resolveCellNumber()
|
||||||
if (!cellNumber) return
|
if (!cellNumber) return
|
||||||
|
|
||||||
@@ -205,6 +217,8 @@ export function LoginPage() {
|
|||||||
domain: tenantDomain,
|
domain: tenantDomain,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
setCellExistsSites(null)
|
||||||
|
|
||||||
if (data.user.customerBusinesses.length === 0) {
|
if (data.user.customerBusinesses.length === 0) {
|
||||||
logoutRequest()
|
logoutRequest()
|
||||||
setError(CUSTOMER_ACCESS_MESSAGE)
|
setError(CUSTOMER_ACCESS_MESSAGE)
|
||||||
@@ -213,35 +227,101 @@ export function LoginPage() {
|
|||||||
|
|
||||||
if (!data.user.cellVerifiedAt) {
|
if (!data.user.cellVerifiedAt) {
|
||||||
logoutRequest()
|
logoutRequest()
|
||||||
setView('otp')
|
await continueToSmsVerify(cellNumber)
|
||||||
setSmsStep('phone')
|
|
||||||
setPassword('')
|
|
||||||
setConfirmPassword('')
|
|
||||||
setSmsCode('')
|
|
||||||
setInfo(t('signup.info.verify'))
|
|
||||||
try {
|
|
||||||
const otpResult = await sendOtp(cellNumber, tenantDomain)
|
|
||||||
if (!otpResult.enabled) {
|
|
||||||
setInfo(otpResult.message)
|
|
||||||
}
|
|
||||||
setCodeSent(true)
|
|
||||||
setSmsStep('code')
|
|
||||||
startCountdown()
|
|
||||||
} catch (otpErr) {
|
|
||||||
handleApiError(otpErr, t('login.error.sendCode'))
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await login(cellNumber, password)
|
await login(cellNumber, password)
|
||||||
navigate(redirectTo)
|
navigate(redirectTo)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
if (err instanceof ApiError) {
|
||||||
|
const sites = parseCellExistsSites(err.message)
|
||||||
|
if (sites !== null) {
|
||||||
|
setCellExistsSites(sites)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setCellExistsSites(null)
|
||||||
handleApiError(err, t('signup.error.create'))
|
handleApiError(err, t('signup.error.create'))
|
||||||
} finally {
|
} finally {
|
||||||
setIsSubmitting(false)
|
setIsSubmitting(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function continueToSmsVerify(cellNumber: string) {
|
||||||
|
setView('otp')
|
||||||
|
setSmsStep('phone')
|
||||||
|
setPassword('')
|
||||||
|
setConfirmPassword('')
|
||||||
|
setSmsCode('')
|
||||||
|
setInfo(t('signup.info.verify'))
|
||||||
|
try {
|
||||||
|
const otpResult = await sendOtp(cellNumber, tenantDomain)
|
||||||
|
if (!otpResult.enabled) {
|
||||||
|
setInfo(otpResult.message)
|
||||||
|
}
|
||||||
|
setCodeSent(true)
|
||||||
|
setSmsStep('code')
|
||||||
|
startCountdown()
|
||||||
|
} catch (otpErr) {
|
||||||
|
handleApiError(otpErr, t('login.error.sendCode'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSignup(e: React.FormEvent) {
|
||||||
|
e.preventDefault()
|
||||||
|
clearMessages()
|
||||||
|
setCellExistsSites(null)
|
||||||
|
|
||||||
|
if (password !== confirmPassword) {
|
||||||
|
setError(t('signup.error.match'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isPasswordStrong(password)) {
|
||||||
|
setError(t('forgot.error.strength'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await runSignup()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Existing Meshkee account on another site: user admits the notice, then proves
|
||||||
|
* phone ownership via SMS. login-otp links them to this website (password/data unchanged),
|
||||||
|
* so they appear in the business Customers list after verification.
|
||||||
|
*/
|
||||||
|
async function handleAdmitCellExists() {
|
||||||
|
clearMessages()
|
||||||
|
const cellNumber = resolveCellNumber()
|
||||||
|
if (!cellNumber) {
|
||||||
|
setCellExistsSites(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsSubmitting(true)
|
||||||
|
setCellExistsSites(null)
|
||||||
|
try {
|
||||||
|
setInfo(t('signup.info.verifyExisting'))
|
||||||
|
setView('otp')
|
||||||
|
setSmsStep('phone')
|
||||||
|
setPassword('')
|
||||||
|
setConfirmPassword('')
|
||||||
|
setSmsCode('')
|
||||||
|
const otpResult = await sendOtp(cellNumber, tenantDomain)
|
||||||
|
if (!otpResult.enabled) {
|
||||||
|
setInfo(otpResult.message)
|
||||||
|
}
|
||||||
|
setCodeSent(true)
|
||||||
|
setSmsStep('code')
|
||||||
|
startCountdown()
|
||||||
|
} catch (otpErr) {
|
||||||
|
handleApiError(otpErr, t('login.error.sendCode'))
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleResetPassword(e: React.FormEvent) {
|
async function handleResetPassword(e: React.FormEvent) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
clearMessages()
|
clearMessages()
|
||||||
@@ -842,6 +922,56 @@ export function LoginPage() {
|
|||||||
>
|
>
|
||||||
Powered by Meshkee.app
|
Powered by Meshkee.app
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
{cellExistsSites !== null &&
|
||||||
|
createPortal(
|
||||||
|
<div
|
||||||
|
className={styles.noticeOverlay}
|
||||||
|
onClick={() => !isSubmitting && setCellExistsSites(null)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={styles.noticeModal}
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-labelledby="cell-exists-title"
|
||||||
|
lang={locale}
|
||||||
|
dir={dir}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<div className={styles.noticeHeader}>
|
||||||
|
<h2 id="cell-exists-title" className={styles.noticeTitle}>
|
||||||
|
{t('signup.cellExists.title')}
|
||||||
|
</h2>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={styles.noticeClose}
|
||||||
|
onClick={() => setCellExistsSites(null)}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
aria-label={t('common.close')}
|
||||||
|
>
|
||||||
|
<X size={20} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p className={styles.noticeBody}>
|
||||||
|
{renderWithMeshkeeLink(
|
||||||
|
cellExistsSites
|
||||||
|
? t('signup.error.cellExists', { sites: cellExistsSites })
|
||||||
|
: t('signup.error.cellExistsGeneric'),
|
||||||
|
styles.noticeLink,
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={styles.noticeAdmit}
|
||||||
|
onClick={() => void handleAdmitCellExists()}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
>
|
||||||
|
{isSubmitting ? t('signup.creating') : t('signup.cellExists.admit')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
document.body,
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export async function register(input: {
|
|||||||
lastName: string
|
lastName: string
|
||||||
email?: string
|
email?: string
|
||||||
domain: string
|
domain: string
|
||||||
|
acknowledgeExistingAccount?: boolean
|
||||||
}) {
|
}) {
|
||||||
const data = await apiRequest<RegisterResponse>('/auth/register', {
|
const data = await apiRequest<RegisterResponse>('/auth/register', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
Reference in New Issue
Block a user