fix(workspaces): streamline member import modal
Some checks failed
Frontend CI/CD / build (push) Has been cancelled
Frontend CI/CD / deploy (push) Has been cancelled

This commit is contained in:
2026-06-19 01:48:12 +03:30
parent c7ede31b68
commit 2e7ea40a79
4 changed files with 461 additions and 355 deletions

View File

@@ -283,50 +283,79 @@ export const fa = {
statsOwnersAdmins: "مالکان و ادمین‌ها",
statsGuests: "مهمان‌ها",
membersSectionTitle: "اعضا",
membersSectionSubtitle: "اعضای این ورک‌اسپیس و نقش فعلی آن‌ها.",
membersLocked: "فهرست کامل اعضا فقط برای مالک و ادمین قابل مشاهده است.",
projectRateHint: "برای هر کاربر می‌توانید از صفحه پروژه‌ها و داخل پنجره دسترسی پروژه، یک نرخ اختصاصی برای همان پروژه تعریف کنید تا روی نرخ ساعتی ورک‌اسپیس اولویت داشته باشد.",
memberImport: {
button: "درون‌ریزی اعضا",
title: "درون‌ریزی اعضا",
description: "فایلی با ستون‌های mobile، role، hourly_rate و currency بارگذاری کنید. موبایل الزامی است و نقش در صورت خالی بودن عضو در نظر گرفته می‌شود.",
uploadTitle: "بارگذاری فایل اعضا",
uploadDescription: "فرمت‌های CSV، TSV، TXT یا XLSX پشتیبانی می‌شوند. ردیف اول باید عنوان ستون‌ها باشد.",
sampleCsv: "نمونه CSV",
sampleTsv: "نمونه TSV",
sampleTxt: "نمونه TXT",
sampleXlsx: "نمونه XLSX",
validate: "اعتبارسنجی فایل",
validating: "در حال اعتبارسنجی...",
import: "درون‌ریزی اعضا",
importing: "در حال درون‌ریزی...",
chooseFile: "انتخاب فایل",
selectedFile: "فایل انتخاب‌شده",
validRows: "ردیف‌های معتبر",
invalidRows: "ردیف‌های نامعتبر",
totalRows: "کل ردیف‌ها",
line: "ردیف",
mobile: "موبایل",
user: "کاربر",
role: "نقش",
hourlyRate: "نرخ ساعتی",
currency: "واحد پول",
status: "وضعیت",
messages: "پیام‌ها",
valid: "معتبر",
invalid: "نامعتبر",
noRows: "هنوز ردیفی بارگذاری نشده است.",
localErrors: "قبل از اعتبارسنجی سمت سرور، خطاهای فایل را اصلاح کنید.",
success: "اعضا با موفقیت درون‌ریزی شدند.",
parseFailed: "خواندن فایل ناموفق بود.",
missingMobile: "موبایل الزامی است.",
duplicateMobile: "این موبایل بیش از یک بار در فایل آمده است.",
invalidRole: "نقش باید admin، member یا guest باشد.",
invalidRate: "نرخ ساعتی باید عددی معتبر و بزرگ‌تر از صفر باشد.",
rateCurrencyPair: "نرخ ساعتی و واحد پول باید با هم وارد شوند.",
tooManyRows: "درون‌ریزی به ۵۰۰ ردیف محدود است.",
},
manageMembers: "مدیریت اعضا",
membersSectionSubtitle: "اعضای این ورک‌اسپیس و نقش فعلی آن‌ها.",
membersLocked: "فهرست کامل اعضا فقط برای مالک و ادمین قابل مشاهده است.",
projectRateHint: "برای هر کاربر می‌توانید از صفحه پروژه‌ها و داخل پنجره دسترسی پروژه، یک نرخ اختصاصی برای همان پروژه تعریف کنید تا روی نرخ ساعتی ورک‌اسپیس اولویت داشته باشد.",
memberImport: {
button: "اضافه‌کردن گروهی",
title: "اضافه‌کردن گروهی",
description: "فایلی با ستون‌های mobile، role، hourly_rate و currency بارگذاری کنید. موبایل الزامی است و نقش در صورت خالی بودن عضو در نظر گرفته می‌شود.",
uploadTitle: "بارگذاری فایل اعضا",
uploadDescription: "فرمت‌های CSV، TSV، TXT یا XLSX پشتیبانی می‌شوند. ردیف اول باید عنوان ستون‌ها باشد.",
sampleCsv: "نمونه CSV",
sampleTsv: "نمونه TSV",
sampleTxt: "نمونه TXT",
sampleXlsx: "نمونه XLSX",
validate: "اعتبارسنجی فایل",
validating: "در حال اعتبارسنجی...",
import: "اضافه‌کردن گروهی",
importing: "در حال اضافه‌کردن...",
chooseFile: "انتخاب فایل",
selectedFile: "فایل انتخاب‌شده",
validRows: "ردیف‌های معتبر",
invalidRows: "ردیف‌های نامعتبر",
totalRows: "کل ردیف‌ها",
line: "ردیف",
mobile: "موبایل",
user: "کاربر",
role: "نقش",
hourlyRate: "نرخ ساعتی",
currency: "واحد پول",
status: "وضعیت",
messages: "پیام‌ها",
valid: "معتبر",
invalid: "نامعتبر",
noRows: "هنوز ردیفی بارگذاری نشده است.",
localErrors: "قبل از اعتبارسنجی سمت سرور، خطاهای فایل را اصلاح کنید.",
helpTitle: "راهنمای اضافه‌کردن گروهی",
helpDescription: "برای آماده‌کردن فایل معتبر، این نکات را رعایت کنید.",
helpCurrencyTitle: "واحدهای پول معتبر",
helpRolesTitle: "نقش‌های معتبر",
roleAdminDescription: "می‌تواند تنظیمات، اعضا، پروژه‌ها، گزارش‌ها و داده‌های مشترک ورک‌اسپیس را مدیریت کند.",
roleMemberDescription: "می‌تواند به‌صورت عادی از ورک‌اسپیس استفاده کند و برای پروژه‌های در دسترس زمان ثبت کند.",
roleGuestDescription: "دسترسی محدود دارد و فقط برای پروژه‌هایی که به او دسترسی داده شده زمان ثبت می‌کند.",
helpMobilePrefix: "ستون",
helpMobileSuffix: "الزامی است و باید متعلق به یک کاربر ثبت‌نام‌شده باشد.",
helpRolePrefix: "ستون",
helpRoleSuffix: "اختیاری است. اگر خالی باشد member در نظر گرفته می‌شود.",
helpRatePrefix: "ستون‌های",
helpRateMiddle: "و",
helpRateSuffix: "اختیاری هستند، اما اگر یکی پر شود دیگری هم باید پر شود.",
helpSamplesTitle: "فایل‌های نمونه",
success: "اعضا با موفقیت درون‌ریزی شدند.",
parseFailed: "خواندن فایل ناموفق بود.",
missingMobile: "موبایل الزامی است.",
duplicateMobile: "این موبایل بیش از یک بار در فایل آمده است.",
invalidRole: "نقش باید admin، member یا guest باشد.",
invalidRate: "نرخ ساعتی باید عددی معتبر و بزرگ‌تر از صفر باشد.",
rateCurrencyPair: "نرخ ساعتی و واحد پول باید با هم وارد شوند.",
tooManyRows: "درون‌ریزی به ۵۰۰ ردیف محدود است.",
messagesMap: {
too_many_rows: "اضافه‌کردن گروهی به ۵۰۰ ردیف محدود است.",
mobile_required: "موبایل الزامی است.",
duplicate_mobile: "این موبایل بیش از یک‌بار در فایل آمده است.",
user_not_found: "کاربر ثبت‌نام‌شده‌ای با این موبایل پیدا نشد.",
already_member: "این کاربر از قبل عضو این ورک‌اسپیس است.",
owner_role_not_allowed: "نقش مالک را نمی‌توان از طریق فایل اضافه کرد.",
invalid_role: "نقش باید admin، member یا guest باشد.",
role_permission_denied: "شما اجازه اختصاص این نقش را ندارید.",
rate_currency_pair_required: "نرخ ساعتی و واحد پول باید با هم وارد شوند.",
hourly_rate_positive: "نرخ ساعتی باید بزرگ‌تر از صفر باشد.",
hourly_rate_invalid: "نرخ ساعتی باید یک عدد معتبر باشد.",
currency_invalid: "واحد پول معتبر نیست.",
},
},
manageMembers: "مدیریت اعضا",
mobileNumber: "شماره تماس",
youLabel: "شما",
resourcesTitle: "منابع",
@@ -341,10 +370,10 @@ export const fa = {
},
createdSuccess: "ورک‌اسپیس با موفقیت ایجاد شد",
updatedSuccess: "ورک‌اسپیس با موفقیت ویرایش شد",
fetchError: "خطا در دریافت اطلاعات ورک‌اسپیس",
loadErrorDescription: "ممکن است سرویس بک‌اند در دسترس نباشد. لطفاً چند لحظه بعد دوباره تلاش کنید.",
retry: "تلاش دوباره",
remove: "حذف",
fetchError: "خطا در دریافت اطلاعات ورک‌اسپیس",
loadErrorDescription: "ممکن است سرویس بک‌اند در دسترس نباشد. لطفاً چند لحظه بعد دوباره تلاش کنید.",
retry: "تلاش دوباره",
remove: "حذف",
noUsersFound: "کاربری یافت نشد",
selectRole: "انتخاب نقش",
add: "افزودن",
@@ -433,14 +462,14 @@ export const fa = {
collapse: 'جمع کردن',
},
landing: {
landing: {
brandLabel: "زیرساخت عملیاتی زمان",
eyebrow: "طراحی‌شده برای تیم‌های دقیق که به داده زمانی قابل اتکا نیاز دارند",
nav: {
demo: "دموی محصول",
features: "قابلیت‌ها",
workflow: "فرآیند کار",
about: "درباره ما",
demo: "دموی محصول",
features: "قابلیت‌ها",
workflow: "فرآیند کار",
about: "درباره ما",
},
actions: {
switchToEnglish: "English",
@@ -449,9 +478,9 @@ export const fa = {
openApp: "ورود به اپ",
openWorkspace: "باز کردن ورک‌اسپیس",
startNow: "شروع با کنترل کامل",
watchDemo: "مشاهده دموی محصول",
readTerms: "مطالعه قوانین",
readAbout: "درباره Qlockify",
watchDemo: "مشاهده دموی محصول",
readTerms: "مطالعه قوانین",
readAbout: "درباره Qlockify",
},
hero: {
titleTop: "هر ساعت کاری را به یک سیگنال عملیاتی قابل اعتماد تبدیل کنید.",
@@ -521,16 +550,16 @@ export const fa = {
finalCtaTitle: "اگر تیم شما تخصص می‌فروشد یا پروژه مشتری تحویل می‌دهد، سیستم زمان شما هم باید همین‌قدر جدی باشد.",
finalCtaDescription:
"اپ را باز کنید، ورک‌اسپیس بسازید و ببینید وقتی محصول نشت بستر را متوقف می‌کند، انضباط گزارش‌دهی چقدر سریع بهتر می‌شود.",
},
demo: {
badge: "محیط دمو",
starting: "در حال آماده‌سازی دمو...",
started: "محیط دمو آماده شد.",
startError: "امکان ساخت محیط دمو وجود ندارد.",
expiresAt: "زمان انقضا",
resetAction: "شروع دوباره دمو",
reset: "محیط دموی تازه آماده شد.",
},
},
demo: {
badge: "محیط دمو",
starting: "در حال آماده‌سازی دمو...",
started: "محیط دمو آماده شد.",
startError: "امکان ساخت محیط دمو وجود ندارد.",
expiresAt: "زمان انقضا",
resetAction: "شروع دوباره دمو",
reset: "محیط دموی تازه آماده شد.",
},
ordering: {
createdAtDesc: "جدیدترین",
@@ -677,10 +706,10 @@ export const fa = {
timesheet: {
title: "تایم‌شیت",
description: (workspaceName: string) => `ثبت زمان در ${workspaceName}`,
selectWorkspace: "لطفاً ابتدا یک ورک‌اسپیس انتخاب کنید.",
addEntry: "افزودن ورودی",
addManualEntry: "افزودن دستی زمان",
startTimer: "شروع تایمر",
selectWorkspace: "لطفاً ابتدا یک ورک‌اسپیس انتخاب کنید.",
addEntry: "افزودن ورودی",
addManualEntry: "افزودن زمان",
startTimer: "شروع تایمر",
stopTimer: "توقف تایمر",
timerRunning: "تایمر فعال است",
runningLabel: "تایمر فعلی",
@@ -693,9 +722,9 @@ export const fa = {
emptyDescription: "بدون توضیح",
emptyStateDescription: "برای شروع، تایمر را اجرا کنید یا یک ورودی دستی اضافه کنید.",
noEntriesSearch: "عبارت جست‌وجو یا فیلترهای خود را تغییر دهید.",
createTitle: "افزودن ورودی زمان",
manualCreateTitle: "افزودن دستی زمان",
startTitle: "شروع تایمر",
createTitle: "افزودن ورودی زمان",
manualCreateTitle: "افزودن زمان",
startTitle: "شروع تایمر",
editTitle: "ویرایش ورودی زمان",
createSuccess: "ورودی زمان با موفقیت ایجاد شد.",
startSuccess: "تایمر با موفقیت شروع شد.",
@@ -737,14 +766,14 @@ export const fa = {
searchTagsLabel: "جست‌وجوی تگ‌ها...",
noTagsFoundLabel: "تگی پیدا نشد.",
searchProjectsLabel: "جست‌وجوی پروژه‌ها...",
noProjectsFoundLabel: "پروژه‌ای پیدا نشد.",
deletedProjectLabel: "پروژه حذف‌شده",
deletedTagLabel: "تگ حذف‌شده",
startRequiredError: "تاریخ و زمان شروع الزامی است.",
endRequiredError: "تاریخ و زمان پایان باید هر دو وارد شوند.",
invalidEndTimeError: "زمان پایان معتبر نیست.",
endBeforeStartError: "پایان باید بعد از شروع باشد.",
},
noProjectsFoundLabel: "پروژه‌ای پیدا نشد.",
deletedProjectLabel: "پروژه حذف‌شده",
deletedTagLabel: "تگ حذف‌شده",
startRequiredError: "تاریخ و زمان شروع الزامی است.",
endRequiredError: "تاریخ و زمان پایان باید هر دو وارد شوند.",
invalidEndTimeError: "زمان پایان معتبر نیست.",
endBeforeStartError: "پایان باید بعد از شروع باشد.",
},
reports: {
title: "گزارش‌ها",
description: (workspaceName: string) => `مرور گزارش فعالیت برای ${workspaceName}`,