feat(auth): add stepped auth and password recovery flows

This commit is contained in:
2026-05-03 17:10:02 +03:30
parent 9b1cd772fb
commit 380b794ab1
19 changed files with 1857 additions and 687 deletions

View File

@@ -11,8 +11,8 @@ export const fa = {
save: "ذخیره",
remove: "حذف",
lightMode: "حالت روشن",
darkMode: "حالت تاریک",
settings: "تنظیمات",
darkMode: "حالت تاریک",
settings: "تنظیمات",
loadingText: "در حال بارگذاری...",
loading: "در حال بارگذاری...",
noMoreResults: "نتیجه دیگری نیست.",
@@ -26,65 +26,102 @@ export const fa = {
},
login: {
loginTitle: "ورود به حساب",
loginDescription: "شماره موبایل خود را وارد کنید تا کد یکبار مصرف برای شما ارسال شود.",
loginCta: "ورود",
createAccount: "ایجاد حساب",
haveNoAccount: "حساب ندارید؟",
haveAccount: "قبلا حساب دارید؟",
loginOtpTitle: "کد ورود را تایید کنید",
passwordLoginTitle: "ورود با رمز عبور",
passwordLoginDescription: (mobile: string) => `رمز عبور مربوط به ${mobile} را وارد کنید`,
usePasswordInstead: "استفاده از رمز عبور",
useOtpInstead: "استفاده از کد یکبار مصرف",
backToMobile: "بازگشت به مرحله موبایل",
backToPasswordLogin: "بازگشت به ورود با رمز عبور",
forgotPassword: "رمز عبور را فراموش کرده‌اید؟",
signupTitle: "ساخت حساب جدید",
signupDescription: "با شماره موبایل شروع کنید تا کد تایید برای شما ارسال شود.",
sendSignupCode: "ارسال کد تایید",
signupVerifyTitle: "تایید شماره موبایل",
continueToPassword: "ادامه به مرحله رمز عبور",
signupPasswordTitle: "تعیین رمز عبور",
signupPasswordDescription: "برای حساب جدید خود یک رمز عبور انتخاب کنید.",
createAccountPasswordCta: "ایجاد حساب",
forgotPasswordTitle: "بازیابی رمز عبور",
forgotPasswordDescription: "شماره موبایل خود را وارد کنید تا کد تایید برای تغییر رمز عبور ارسال شود.",
sendResetCode: "ارسال کد بازیابی",
forgotPasswordVerifyTitle: "کد بازیابی را وارد کنید",
continueToResetPassword: "ادامه برای تعیین رمز جدید",
resetPasswordTitle: "انتخاب رمز عبور جدید",
resetPasswordDescription: "رمز عبور جدید خود را وارد کنید و آن را تایید کنید.",
resetPasswordCta: "تغییر رمز عبور",
newPasswordPlaceholder: "رمز عبور جدید",
confirmPasswordPlaceholder: "تکرار رمز عبور",
passwordMismatch: "رمز عبور و تکرار آن یکسان نیستند.",
welcome: (title: string = "Qlockifiy") => `به ${title} خوش آمدید`,
enterPassword: "رمز عبور خود را وارد کنید",
verifyNumber: "تایید شماره موبایل",
enterMobileDesc: "برای ادامه، شماره موبایل خود را وارد کنید",
signInDesc: "با استفاده از رمز عبور خود وارد شوید",
sentCodeDesc: (mobile: string) => `کد ۶ رقمی به ${mobile} ارسال شد`,
mobilePlaceholder: "شماره موبایل (مثلا ۰۹۱۲۳۴۵۶۷۸۹)",
continueWithPassword: "ادامه با رمز عبور",
continueWithGoogle: "ادامه با گوگل",
orContinueWith: "یا ادامه با",
sentCodeDesc: (mobile: string) => `کد تایید به ${mobile} ارسال شد`,
mobilePlaceholder: "شماره موبایل (مثلا ۰۹۱۲۳۴۵۶۷۸۹)",
continueWithPassword: "ادامه با رمز عبور",
continueWithGoogle: "ادامه با گوگل",
orContinueWith: "یا ادامه با",
otpLogin: "ورود با کد یکبار مصرف",
register: "ثبت نام",
passwordPlaceholder: "رمز عبور",
signIn: "ورود",
back: "بازگشت",
otpPlaceholder: "کد ۶ رقمی",
otpPlaceholder: "کد ۵ رقمی",
verifyAndContinue: "تایید و ادامه",
terms: "با کلیک روی ادامه، شما با قوانین و مقررات و حریم خصوصی ما موافقت می‌کنید.",
brandingQuote: "زمان و ورک‌اسپیس‌ها خود را با پلتفرم مینیمال، سریع و امن ما بهینه مدیریت کنید.",
toasts: {
enterMobile: "لطفا شماره موبایل خود را وارد کنید",
verifySent: "کد تایید ارسال شد!",
failedOtp: "ارسال کد تایید با خطا مواجه شد",
fillAll: "لطفا تمام فیلدها را پر کنید",
successLogin: "با موفقیت وارد شدید!",
invalidCreds: "اطلاعات ورود نامعتبر است",
enterOtp: "لطفا کد تایید را وارد کنید",
invalidOtp: "کد تایید نامعتبر است"
},
throttle: {
title: "تعداد تلاش‌ها بیش از حد مجاز است",
toasts: {
enterMobile: "لطفا شماره موبایل خود را وارد کنید",
verifySent: "کد تایید ارسال شد.",
failedOtp: "ارسال کد تایید انجام نشد.",
fillAll: "لطفا تمام فیلدها را پر کنید.",
successLogin: "با موفقیت وارد شدید.",
accountCreated: "حساب با موفقیت ایجاد شد.",
failedSignup: "تکمیل ثبت نام انجام نشد.",
invalidCreds: "اطلاعات ورود نامعتبر است.",
enterOtp: "لطفا کد تایید را وارد کنید.",
invalidOtp: "کد تایید نامعتبر است.",
passwordResetSuccess: "رمز عبور با موفقیت تغییر کرد.",
passwordResetFailed: "تغییر رمز عبور انجام نشد."
},
throttle: {
title: "تعداد تلاش‌ها بیش از حد مجاز است",
genericMessage: (time: string) => `درخواست‌های زیادی ارسال شده است. ${time} دیگر دوباره تلاش کنید.`,
otpSendMessage: (time: string) => `ارسال کد یکبار مصرف بیش از حد مجاز انجام شده است. ${time} دیگر دوباره تلاش کنید.`,
passwordLoginMessage: (time: string) => `تلاش برای ورود با رمز عبور بیش از حد مجاز بوده است. ${time} دیگر دوباره تلاش کنید.`,
otpLoginMessage: (time: string) => `تلاش برای ورود با کد یکبار مصرف بیش از حد مجاز بوده است. ${time} دیگر دوباره تلاش کنید.`,
countdownLabel: (time: string) => `تلاش دوباره تا ${time}`,
fallback: "درخواست‌های زیادی ارسال شده است. کمی صبر کنید و دوباره تلاش کنید.",
},
google: {
loadingTitle: "در حال تکمیل ورود با گوگل",
loadingDescription: "در حال بررسی حساب گوگل شما و آماده‌سازی مرحله بعد هستیم.",
collectMobileTitle: "ساخت حساب را کامل کنید",
collectMobileDescription: (email: string) =>
`حساب گوگل ${email} تایید شد. برای تکمیل ساخت حساب، شماره موبایل خود را وارد کنید.`,
claimTitle: "حساب موجود خود را تایید کنید",
claimDescription: (mobile: string) =>
`حسابی با شماره ${mobile} از قبل وجود دارد. کد ارسال‌شده به این شماره را وارد کنید تا گوگل به همان حساب متصل شود.`,
errorTitle: "ورود با گوگل کامل نشد",
missingFlow: "جریان ورود با گوگل پیدا نشد یا منقضی شده است.",
loadFailed: "بارگذاری وضعیت ورود با گوگل انجام نشد.",
completeFailed: "تکمیل ساخت حساب با گوگل انجام نشد.",
claimOtpSent: "کد تایید با موفقیت ارسال شد.",
googleAccount: "حساب گوگل",
completeButton: "ادامه و ایجاد حساب",
verifyClaimButton: "تایید و ادامه",
resendClaimOtp: "ارسال دوباره کد تایید",
restartGoogle: "شروع دوباره ورود با گوگل",
}
},
fallback: "درخواست‌های زیادی ارسال شده است. کمی صبر کنید و دوباره تلاش کنید.",
},
google: {
loadingTitle: "در حال تکمیل ورود با گوگل",
loadingDescription: "در حال بررسی حساب گوگل شما و آماده‌سازی مرحله بعد هستیم.",
collectMobileTitle: "ساخت حساب را کامل کنید",
collectMobileDescription: (email: string) =>
`حساب گوگل ${email} تایید شد. برای تکمیل ساخت حساب، شماره موبایل خود را وارد کنید.`,
claimTitle: "حساب موجود خود را تایید کنید",
claimDescription: (mobile: string) =>
`حسابی با شماره ${mobile} از قبل وجود دارد. کد ارسال‌شده به این شماره را وارد کنید تا گوگل به همان حساب متصل شود.`,
errorTitle: "ورود با گوگل کامل نشد",
missingFlow: "جریان ورود با گوگل پیدا نشد یا منقضی شده است.",
loadFailed: "بارگذاری وضعیت ورود با گوگل انجام نشد.",
completeFailed: "تکمیل ساخت حساب با گوگل انجام نشد.",
claimOtpSent: "کد تایید با موفقیت ارسال شد.",
googleAccount: "حساب گوگل",
completeButton: "ادامه و ایجاد حساب",
verifyClaimButton: "تایید و ادامه",
resendClaimOtp: "ارسال دوباره کد تایید",
restartGoogle: "شروع دوباره ورود با گوگل",
}
},
loginTerms: {
prefix: "با ورود به سیستم، شما با ",
@@ -156,17 +193,31 @@ export const fa = {
changePicture: "تغییر تصویر",
save: "ذخیره",
cancel: "لغو",
upload: "آپلود",
remove: "حذف",
imageInput: "برای انتخاب کلیک کنید یا فایل را بکشید",
noEmail: "ایمیلی ثبت نشده",
toasts: {
successEdit: "پروفایل با موفقیت بروزرسانی شد!",
successImage: "عکس پروفایل بروزرسانی شد!",
successRemoveImage: "عکس پروفایل حذف شد!",
error: "خطایی رخ داد!"
}
},
upload: "آپلود",
remove: "حذف",
imageInput: "برای انتخاب کلیک کنید یا فایل را بکشید",
noEmail: "ایمیلی ثبت نشده",
password: {
trigger: "تغییر رمز عبور",
title: "تغییر رمز عبور",
description: "رمز عبور فعلی خود را وارد کنید و یک رمز جدید انتخاب کنید.",
currentPassword: "رمز عبور فعلی",
newPassword: "رمز عبور جدید",
confirmPassword: "تکرار رمز جدید",
submit: "ذخیره رمز عبور",
saving: "در حال ذخیره...",
toasts: {
success: "رمز عبور با موفقیت تغییر کرد.",
error: "تغییر رمز عبور انجام نشد.",
},
},
toasts: {
successEdit: "پروفایل با موفقیت به‌روزرسانی شد.",
successImage: "عکس پروفایل به‌روزرسانی شد.",
successRemoveImage: "عکس پروفایل حذف شد.",
error: "خطایی رخ داد."
}
},
workspace: {
title: "مدیریت ورک‌اسپیس‌ها",
@@ -322,95 +373,95 @@ export const fa = {
collapse: 'جمع کردن',
},
landing: {
brandLabel: "زیرساخت عملیاتی زمان",
eyebrow: "طراحی‌شده برای تیم‌های دقیق که به داده زمانی قابل اتکا نیاز دارند",
nav: {
demo: "دموی محصول",
features: "قابلیت‌ها",
workflow: "فرآیند کار",
},
actions: {
switchToEnglish: "English",
switchToPersian: "فارسی",
signIn: "ورود",
openApp: "ورود به اپ",
openWorkspace: "باز کردن ورک‌اسپیس",
startNow: "شروع با کنترل کامل",
watchDemo: "مشاهده دموی محصول",
readTerms: "مطالعه قوانین",
},
hero: {
titleTop: "هر ساعت کاری را به یک سیگنال عملیاتی قابل اعتماد تبدیل کنید.",
titleAccent: "Qlockify زمان را شفاف، پاسخ‌گو و قابل‌صورتحساب می‌کند.",
description:
"یک محیط متمرکز برای تیم‌های مدرن که به ثبت سریع زمان، رهگیری دقیق پروژه، گزارش‌های قابل اتکا و لاگ عملیاتی واقعی برای مدیریت نیاز دارند.",
},
metrics: {
capture: "ثبت تمیزتر ساعات قابل‌صورتحساب",
visibility: "دسترسی سریع‌تر به دید گزارش‌دهی",
decision: "از ورودی خام تا تصمیم مدیریتی",
},
trust: {
first: "تایمر دقیق با امکان ویرایش دستی در زمان لازم",
second: "دسترسی‌ها، لاگ‌ها و گزارش‌های مبتنی بر نرخ",
third: "مناسب آژانس‌ها، شرکت‌های مشاوره، تیم‌های محصول و عملیات",
},
capabilities: {
time: {
title: "ثبت کار بدون اصطکاک",
description:
"تایمر را شروع کنید، ورودی‌های گذشته را اصلاح کنید و پروژه و تگ را بدون ایجاد اصطکاک برای تیم به هر ساعت متصل نگه دارید.",
},
reports: {
title: "کسب‌وکار را در چند دقیقه بخوانید",
description:
"خروجی روزانه، عملکرد قابل‌صورتحساب، توزیع پروژه‌ها و بسته‌های گزارشی قابل خروجی را بدون پاک‌سازی دستی فایل‌ها ببینید.",
},
control: {
title: "عملیات را قابل توضیح نگه دارید",
description:
"ببینید چه کسی چه چیزی را تغییر داده، نقش‌ها را شفاف نگه دارید و برای مدیریت یک رد عملیاتی تمیزتر از چت و فایل دستی بسازید.",
},
},
demo: {
timerTag: "تایمر زنده",
timerTitle: "بازه اجرای فعلی",
timerText: "بهبود دیزاین سیستم، متصل به پروژه درست، تگ‌های صحیح و نرخ قابل‌صورتحساب.",
panelLabel: "پیش‌نمایش تعاملی محصول",
panelTitle: "یک سطح واحد برای رهگیری، گزارش‌دهی و شفافیت عملیاتی",
runningCard: "ورودی فعال",
currentTask: "پیاده‌سازی لندینگ سازمانی",
currentTaskMeta: "پروژه: بازاریابی Qlockify · تگ‌ها: طراحی، بازبینی، تحویل",
billableLabel: "نرخ زنده قابل‌صورتحساب",
reportCard: "روند گزارش روزانه",
opsCard: "سلامت عملیات",
opsLabels: ["پوشش", "تمرکز تیم", "آمادگی صورتحساب"],
logCard: "آخرین فعالیت‌های ورک‌اسپیس",
logItems: [
{ title: "نرخ تیم طراحی محصول به‌روزرسانی شد", meta: "اقدام مالک · ۳ دقیقه پیش" },
{ title: "پروژه مشتری‌محور بایگانی شد", meta: "اقدام ادمین · ۱۸ دقیقه پیش" },
{ title: "تگ تاریخی روی ورودی ویرایش‌شده حفظ شد", meta: "اقدام عضو · ۴۱ دقیقه پیش" },
],
outcomeTag: "خروجی مدیریتی",
outcomeText: "ابهام کمتر در پایان ماه، ساعات قابل‌صورتحساب از‌دست‌رفته کمتر و بازبینی عملیاتی سریع‌تر.",
},
workflowTag: "فرآیند عملیاتی",
workflowTitle: "از تلاش خام تا داده مدیریتی قابل استفاده، با یک حلقه کوتاه‌تر.",
workflowDescription:
"Qlockify مسیر را کوتاه نگه می‌دارد: دقیق ثبت کنید، یک‌بار بستر را درست بسازید و همان نتیجه را در تایم‌شیت، گزارش و تصمیم‌گیری مدیریتی مصرف کنید.",
workflow: {
capture: "زمان را در مبدأ، همراه با پروژه، تگ و بستر مالی ثبت کنید.",
structure: "هر تغییر در اعضا، نرخ‌ها و تنظیمات ورک‌اسپیس را قابل مشاهده و قابل بررسی نگه دارید.",
improve: "عملکرد روزانه و ماهانه را با گزارش‌هایی بخوانید که آماده خروجی و اقدام هستند.",
},
finalCtaTag: "آماده برای تیم‌های جدی",
finalCtaTitle: "اگر تیم شما تخصص می‌فروشد یا پروژه مشتری تحویل می‌دهد، سیستم زمان شما هم باید همین‌قدر جدی باشد.",
finalCtaDescription:
"اپ را باز کنید، ورک‌اسپیس بسازید و ببینید وقتی محصول نشت بستر را متوقف می‌کند، انضباط گزارش‌دهی چقدر سریع بهتر می‌شود.",
},
ordering: {
landing: {
brandLabel: "زیرساخت عملیاتی زمان",
eyebrow: "طراحی‌شده برای تیم‌های دقیق که به داده زمانی قابل اتکا نیاز دارند",
nav: {
demo: "دموی محصول",
features: "قابلیت‌ها",
workflow: "فرآیند کار",
},
actions: {
switchToEnglish: "English",
switchToPersian: "فارسی",
signIn: "ورود",
openApp: "ورود به اپ",
openWorkspace: "باز کردن ورک‌اسپیس",
startNow: "شروع با کنترل کامل",
watchDemo: "مشاهده دموی محصول",
readTerms: "مطالعه قوانین",
},
hero: {
titleTop: "هر ساعت کاری را به یک سیگنال عملیاتی قابل اعتماد تبدیل کنید.",
titleAccent: "Qlockify زمان را شفاف، پاسخ‌گو و قابل‌صورتحساب می‌کند.",
description:
"یک محیط متمرکز برای تیم‌های مدرن که به ثبت سریع زمان، رهگیری دقیق پروژه، گزارش‌های قابل اتکا و لاگ عملیاتی واقعی برای مدیریت نیاز دارند.",
},
metrics: {
capture: "ثبت تمیزتر ساعات قابل‌صورتحساب",
visibility: "دسترسی سریع‌تر به دید گزارش‌دهی",
decision: "از ورودی خام تا تصمیم مدیریتی",
},
trust: {
first: "تایمر دقیق با امکان ویرایش دستی در زمان لازم",
second: "دسترسی‌ها، لاگ‌ها و گزارش‌های مبتنی بر نرخ",
third: "مناسب آژانس‌ها، شرکت‌های مشاوره، تیم‌های محصول و عملیات",
},
capabilities: {
time: {
title: "ثبت کار بدون اصطکاک",
description:
"تایمر را شروع کنید، ورودی‌های گذشته را اصلاح کنید و پروژه و تگ را بدون ایجاد اصطکاک برای تیم به هر ساعت متصل نگه دارید.",
},
reports: {
title: "کسب‌وکار را در چند دقیقه بخوانید",
description:
"خروجی روزانه، عملکرد قابل‌صورتحساب، توزیع پروژه‌ها و بسته‌های گزارشی قابل خروجی را بدون پاک‌سازی دستی فایل‌ها ببینید.",
},
control: {
title: "عملیات را قابل توضیح نگه دارید",
description:
"ببینید چه کسی چه چیزی را تغییر داده، نقش‌ها را شفاف نگه دارید و برای مدیریت یک رد عملیاتی تمیزتر از چت و فایل دستی بسازید.",
},
},
demo: {
timerTag: "تایمر زنده",
timerTitle: "بازه اجرای فعلی",
timerText: "بهبود دیزاین سیستم، متصل به پروژه درست، تگ‌های صحیح و نرخ قابل‌صورتحساب.",
panelLabel: "پیش‌نمایش تعاملی محصول",
panelTitle: "یک سطح واحد برای رهگیری، گزارش‌دهی و شفافیت عملیاتی",
runningCard: "ورودی فعال",
currentTask: "پیاده‌سازی لندینگ سازمانی",
currentTaskMeta: "پروژه: بازاریابی Qlockify · تگ‌ها: طراحی، بازبینی، تحویل",
billableLabel: "نرخ زنده قابل‌صورتحساب",
reportCard: "روند گزارش روزانه",
opsCard: "سلامت عملیات",
opsLabels: ["پوشش", "تمرکز تیم", "آمادگی صورتحساب"],
logCard: "آخرین فعالیت‌های ورک‌اسپیس",
logItems: [
{ title: "نرخ تیم طراحی محصول به‌روزرسانی شد", meta: "اقدام مالک · ۳ دقیقه پیش" },
{ title: "پروژه مشتری‌محور بایگانی شد", meta: "اقدام ادمین · ۱۸ دقیقه پیش" },
{ title: "تگ تاریخی روی ورودی ویرایش‌شده حفظ شد", meta: "اقدام عضو · ۴۱ دقیقه پیش" },
],
outcomeTag: "خروجی مدیریتی",
outcomeText: "ابهام کمتر در پایان ماه، ساعات قابل‌صورتحساب از‌دست‌رفته کمتر و بازبینی عملیاتی سریع‌تر.",
},
workflowTag: "فرآیند عملیاتی",
workflowTitle: "از تلاش خام تا داده مدیریتی قابل استفاده، با یک حلقه کوتاه‌تر.",
workflowDescription:
"Qlockify مسیر را کوتاه نگه می‌دارد: دقیق ثبت کنید، یک‌بار بستر را درست بسازید و همان نتیجه را در تایم‌شیت، گزارش و تصمیم‌گیری مدیریتی مصرف کنید.",
workflow: {
capture: "زمان را در مبدأ، همراه با پروژه، تگ و بستر مالی ثبت کنید.",
structure: "هر تغییر در اعضا، نرخ‌ها و تنظیمات ورک‌اسپیس را قابل مشاهده و قابل بررسی نگه دارید.",
improve: "عملکرد روزانه و ماهانه را با گزارش‌هایی بخوانید که آماده خروجی و اقدام هستند.",
},
finalCtaTag: "آماده برای تیم‌های جدی",
finalCtaTitle: "اگر تیم شما تخصص می‌فروشد یا پروژه مشتری تحویل می‌دهد، سیستم زمان شما هم باید همین‌قدر جدی باشد.",
finalCtaDescription:
"اپ را باز کنید، ورک‌اسپیس بسازید و ببینید وقتی محصول نشت بستر را متوقف می‌کند، انضباط گزارش‌دهی چقدر سریع بهتر می‌شود.",
},
ordering: {
createdAtDesc: "جدیدترین",
createdAt: "قدیمی‌ترین",
updatedAtDesc: "اخیراً بروزرسانی شده",