Polish Novya UI with brand colors, first-load skeletons, and session transition overlay.
Keep chat pinned to the latest message while AI thinks or streams, and align buttons/inputs with the logo teal/blue focus treatment. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+69
-9
@@ -12,6 +12,12 @@ export function isBackendNetworkError(error: unknown): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
const CONFLICT_ALREADY_EXISTS =
|
||||
/already exists|account with this email|ایمیل.*وجود|قبلاً ثبت/i;
|
||||
|
||||
const INVALID_CREDENTIALS_MESSAGE =
|
||||
'ایمیل یا رمز عبور اشتباه است. اگر حساب ندارید، ابتدا ثبتنام کنید.';
|
||||
|
||||
export function getBackendErrorMessage(
|
||||
error: unknown,
|
||||
fallback = 'خطا در ارتباط با سرور. لطفاً دوباره تلاش کنید.'
|
||||
@@ -20,6 +26,10 @@ export function getBackendErrorMessage(
|
||||
if (error.status === 0 || error.code === 'NETWORK_ERROR') {
|
||||
return 'سرور در دسترس نیست. لطفاً چند لحظه بعد دوباره تلاش کنید.';
|
||||
}
|
||||
// Must run before generic 401 — login failures also return status 401.
|
||||
if (error.code === 'INVALID_CREDENTIALS') {
|
||||
return INVALID_CREDENTIALS_MESSAGE;
|
||||
}
|
||||
if (error.status === 401 || error.code === 'UNAUTHORIZED') {
|
||||
return 'نشست شما منقضی شده است. لطفاً دوباره وارد شوید.';
|
||||
}
|
||||
@@ -29,19 +39,19 @@ export function getBackendErrorMessage(
|
||||
}
|
||||
return 'سرویس هوش مصنوعی موقتاً در دسترس نیست. لطفاً چند لحظه بعد دوباره تلاش کنید.';
|
||||
}
|
||||
if (error.code === 'INVALID_CREDENTIALS') {
|
||||
return 'ایمیل یا رمز عبور اشتباه است. لطفاً دوباره تلاش کنید یا ثبتنام کنید.';
|
||||
}
|
||||
if (error.code === 'RATE_LIMIT_EXCEEDED') {
|
||||
return 'تعداد تلاشها بیش از حد مجاز است. لطفاً کمی صبر کنید.';
|
||||
return 'تعداد تلاشها بیش از حد مجاز است. لطفاً کمی صبر کنید و دوباره تلاش کنید.';
|
||||
}
|
||||
if (error.code === 'CONFLICT') {
|
||||
if (error.code === 'CONFLICT' || error.status === 409) {
|
||||
if (CONFLICT_ALREADY_EXISTS.test(error.message)) {
|
||||
return 'حسابی با این ایمیل از قبل وجود دارد. لطفاً وارد شوید.';
|
||||
}
|
||||
return error.message || 'این عملیات قبلاً انجام شده است.';
|
||||
}
|
||||
if (error.code === 'VALIDATION_ERROR') {
|
||||
return error.message || 'اطلاعات واردشده نامعتبر است.';
|
||||
if (error.code === 'VALIDATION_ERROR' || error.status === 422) {
|
||||
return localizeValidationMessage(error.message) || 'اطلاعات واردشده نامعتبر است.';
|
||||
}
|
||||
return error.message || fallback;
|
||||
return localizeKnownEnglishMessage(error.message) || fallback;
|
||||
}
|
||||
|
||||
if (error instanceof Error && NETWORK_PATTERN.test(error.message)) {
|
||||
@@ -49,8 +59,58 @@ export function getBackendErrorMessage(
|
||||
}
|
||||
|
||||
if (error instanceof Error && error.message) {
|
||||
return error.message;
|
||||
return localizeKnownEnglishMessage(error.message) || error.message;
|
||||
}
|
||||
|
||||
return fallback;
|
||||
}
|
||||
|
||||
/** Login-specific copy; keeps credential failures distinct from session expiry. */
|
||||
export function getLoginErrorMessage(error: unknown): string {
|
||||
if (error instanceof BackendError) {
|
||||
if (error.code === 'INVALID_CREDENTIALS') {
|
||||
return INVALID_CREDENTIALS_MESSAGE;
|
||||
}
|
||||
if (error.status === 401 && /invalid email or password/i.test(error.message)) {
|
||||
return INVALID_CREDENTIALS_MESSAGE;
|
||||
}
|
||||
if (error.code === 'RATE_LIMIT_EXCEEDED') {
|
||||
return 'تعداد تلاشهای ورود زیاد بوده است. لطفاً چند دقیقه صبر کنید و دوباره تلاش کنید.';
|
||||
}
|
||||
if (error.status === 0 || error.code === 'NETWORK_ERROR') {
|
||||
return 'سرور در دسترس نیست. لطفاً اتصال اینترنت را بررسی کنید و دوباره تلاش کنید.';
|
||||
}
|
||||
if (error.code === 'VALIDATION_ERROR' || error.status === 422) {
|
||||
return localizeValidationMessage(error.message) || 'ایمیل یا رمز عبور واردشده نامعتبر است.';
|
||||
}
|
||||
}
|
||||
|
||||
return getBackendErrorMessage(error, 'ورود انجام نشد. لطفاً دوباره تلاش کنید.');
|
||||
}
|
||||
|
||||
function localizeValidationMessage(message: string | undefined): string | null {
|
||||
if (!message) return null;
|
||||
const lower = message.toLowerCase();
|
||||
if (lower.includes('email')) {
|
||||
return 'فرمت ایمیل نامعتبر است.';
|
||||
}
|
||||
if (lower.includes('password')) {
|
||||
return 'رمز عبور نامعتبر است.';
|
||||
}
|
||||
return localizeKnownEnglishMessage(message);
|
||||
}
|
||||
|
||||
function localizeKnownEnglishMessage(message: string | undefined): string | null {
|
||||
if (!message) return null;
|
||||
if (/invalid email or password/i.test(message)) {
|
||||
return INVALID_CREDENTIALS_MESSAGE;
|
||||
}
|
||||
if (CONFLICT_ALREADY_EXISTS.test(message)) {
|
||||
return 'حسابی با این ایمیل از قبل وجود دارد. لطفاً وارد شوید.';
|
||||
}
|
||||
// Prefer Persian UI copy over raw English API text.
|
||||
if (/^[A-Za-z0-9\s.,'"\-_:!?()]+$/.test(message.trim())) {
|
||||
return null;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user