diff --git a/.htaccess b/.htaccess
new file mode 100644
index 0000000..108ccfe
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,14 @@
+
+
+ Options -MultiViews -Indexes
+
+
+ # Handle Front Controller...
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_FILENAME} !-f
+ #RewriteBase /
+ RewriteRule ^ /admin/index.html [L]
+
+
+
+
diff --git a/src/pages/menuPages/configuration/settingGroups.ts b/src/pages/menuPages/configuration/settingGroups.ts
index bea0208..53fecce 100644
--- a/src/pages/menuPages/configuration/settingGroups.ts
+++ b/src/pages/menuPages/configuration/settingGroups.ts
@@ -82,7 +82,7 @@ export const EDITABLE_SETTING_GROUPS: SettingGroupDef[] = [
id: 'features',
title: 'قابلیتهای سیستم',
icon: 'vuesax-linear:setting-2',
- match: (k) => /^(product_|main_wallet_products|price_from_nasiryan|profit_gold)/.test(k)
+ match: (k) => /^(product_|main_wallet_products|profit_gold)/.test(k)
},
{
id: 'misc',
diff --git a/src/pages/menuPages/configuration/settingTypes.ts b/src/pages/menuPages/configuration/settingTypes.ts
index 7d5cf48..68e9608 100644
--- a/src/pages/menuPages/configuration/settingTypes.ts
+++ b/src/pages/menuPages/configuration/settingTypes.ts
@@ -6,18 +6,17 @@ export type SettingFieldType = 'text' | 'number' | 'price' | 'switch' | 'select'
/** کلیدهای legacy — در پنل نمایش داده نمیشوند */
export const HIDDEN_LEGACY_SETTING_KEYS = new Set([
'main_product_price',
- 'montzar_price',
'etehadie_api_url',
'goldir_price',
'price_primary_source',
'price_method',
'montzar_last_product_log',
'montzar_order_response',
- 'service_charge_percentage'
+ 'service_charge_percentage',
])
/** فقط وقتی is_store فعال است در پنل دیده میشوند */
-export const STORE_ONLY_SETTING_KEYS = new Set(['store_status', 'price_from_nasiryan'])
+export const STORE_ONLY_SETTING_KEYS = new Set(['store_status'])
export function isSettingLocked(canEdit: Const['can_edit']): boolean {
return canEdit === 0 || canEdit === false || canEdit === '0'
@@ -90,7 +89,6 @@ const KEY_TYPE_FALLBACK: Record = {
payping_fin_sync_enabled: 'switch',
external_order_api_enabled: 'switch',
is_store: 'switch',
- price_from_nasiryan: 'switch',
banner_enabled: 'switch',
apk_download_link: 'text',
node_client_id: 'text',
@@ -224,34 +222,206 @@ export function switchLabels(key: string | undefined): { on: string; off: string
return { on: 'فعال', off: 'غیرفعال' }
}
-/** توضیحات کامل و ساده برای کلیدهای مهم — اگر description در دیتابیس خالی بود استفاده میشود */
+/** توضیحات ساده و فارسی برای پنل — بر توضیح دیتابیس (که ممکن است نام فنی انگلیسی داشته باشد) اولویت دارد */
const KEY_HELP_FALLBACK: Record = {
+ name_fa:
+ 'نام فارسی برند که در پیامکها، عنوان صفحات و بخشهای عمومی اپ به مشتری نشان داده میشود.',
+ open_register:
+ 'روشن: کاربران جدید میتوانند ثبتنام کنند. خاموش: فقط ورود کاربران قبلی مجاز است.',
+ auto_active_customer:
+ 'روشن: حساب بلافاصله پس از ثبتنام فعال میشود. خاموش: نیاز به تأیید دستی شما در پنل دارد.',
close_price:
- 'فیلتر اصلی فروشگاه. وقتی روشن باشد، خرید و فروش و نمایش قیمت فقط در بازهٔ «ساعت شروع» تا «ساعت پایان» انجام میشود و خارج از این بازه به مشتری پیام «فروشگاه بسته است» نشان داده میشود. اگر خاموش باشد، فروشگاه همیشه باز است (مگر اینکه برای محصولی بهصورت اختصاصی ساعت تعریف کرده باشید).',
+ 'فیلتر اصلی فروشگاه. وقتی روشن باشد، خرید و فروش و نمایش قیمت فقط بین «ساعت شروع» و «ساعت پایان» انجام میشود و خارج از این بازه به مشتری «فروشگاه بسته است» نشان داده میشود. خاموش: محدودیت ساعتی سراسری اعمال نمیشود (مگر برای محصولی ساعت اختصاصی تعریف کرده باشید).',
startDay:
- 'ساعت شروع فعالیت فروشگاه. فقط وقتی «بستن خودکار خارج از ساعت کاری» روشن باشد اعمال میشود. ساعت را از روی ساعتشمار انتخاب کنید (مثلاً ۰۹:۰۰).',
+ 'ساعت شروع فعالیت روزانه فروشگاه. فقط وقتی «بستن خودکار خارج از ساعت کاری» روشن باشد اعمال میشود. از ساعتشمار انتخاب کنید (مثلاً ۰۹:۰۰).',
endDay:
- 'ساعت پایان فعالیت فروشگاه. فقط وقتی «بستن خودکار خارج از ساعت کاری» روشن باشد اعمال میشود. بعد از این ساعت فروشگاه بسته میشود (مثلاً ۱۷:۰۰).',
+ 'ساعت پایان فعالیت روزانه فروشگاه. فقط وقتی «بستن خودکار خارج از ساعت کاری» روشن باشد اعمال میشود. بعد از این ساعت فروشگاه بسته میشود (مثلاً ۱۷:۰۰ یا ۲۲:۰۰).',
show_last_price:
- 'وقتی قیمت زندهٔ لحظهای در دسترس نباشد: اگر روشن باشد، آخرین قیمت ذخیرهشده در دیتابیس به مشتری نشان داده میشود؛ اگر خاموش باشد، بهجای قیمت صفر پیام «مظنه در دسترس نیست» نمایش داده میشود.'
+ 'وقتی قیمت زنده در دسترس نباشد: روشن = آخرین قیمت ذخیرهشده نشان داده میشود؛ خاموش = بهجای صفر پیام «مظنه در دسترس نیست» نمایش داده میشود.',
+ fee_single:
+ 'اختلاف پایه (ریال) بین قیمت خرید و فروش روی مظنه. معمولاً نصف فاصله اسپرد کل است.',
+ buy_fee:
+ 'مبلغ اضافه (ریال) روی قیمت پایه برای سمت خرید. اگر خالی بگذارید، از «اختلاف پایه» استفاده میشود.',
+ sell_fee:
+ 'مبلغ کسر (ریال) از قیمت پایه برای سمت فروش. اگر خالی بگذارید، از «اختلاف پایه» استفاده میشود.',
+ fee_service_charge_percentage:
+ 'درصد کارمزد هر معامله از مبلغ سفارش (اعشاری؛ مثلاً ۰٫۰۰۵ یعنی نیم در هزار).',
+ fee_free:
+ 'تعداد اولین سفارشهای هر کاربر که از کارمزد معاف میشوند. صفر یعنی غیرفعال.',
+ min_withdraw:
+ 'حداقل مبلغ درخواست برداشت (ریال). درخواستهای کمتر پذیرفته نمیشوند.',
+ min_first_buy:
+ 'حداقل مبلغ اولین خرید هر کاربر (ریال).',
+ minimum_sell:
+ 'حداقل مقدار یا مبلغ مجاز برای ثبت سفارش فروش.',
+ balance_report_exclude_customer_id:
+ 'شناسه عددی مشتری که از گزارش تراز و آمار حذف میشود (مثلاً حساب آزمایشی). چند شناسه را با ویرگول جدا کنید.',
+ text_rule:
+ 'متن کامل قوانین و مقررات که در صفحه قوانین اپ به مشتری نشان داده میشود.',
+ text_about:
+ 'متن «درباره ما» شامل معرفی مجموعه، راههای ارتباطی و توضیح خدمات.',
+ referral_id:
+ 'روشن: وارد کردن کد معرف در ثبتنام اجباری است. خاموش: کد معرف اختیاری است.',
+ gift_for_referrer:
+ 'مقدار پاداش (معمولاً وزن فلز به گرم) که پس از ثبتنام موفق معرفشونده به کیف معرف واریز میشود.',
+ gift_for_referral:
+ 'مقدار پاداش (معمولاً وزن فلز) که به کاربر تازهثبتنامشده داده میشود. صفر یعنی بدون پاداش.',
+ register_referral_sms_to_referrer:
+ 'روشن: پس از ثبتنام کاربر جدید، پیامک اطلاعرسانی برای معرف ارسال میشود.',
+ register_referral_bonus_message:
+ 'روشن: متن پاداش معرف در پیامک خوشآمد ثبتنام گنجانده میشود.',
+ referral_limit:
+ 'حداکثر تعداد دعوت مجاز برای هر کاربر. خالی = بدون محدودیت.',
+ referral_bonus_amount:
+ 'مبلغ یا مقدار پاداش معرف برای نمایش در پیامکها و متنهای دعوت.',
+ jibit_verification:
+ 'روشن: هنگام ثبت یا ویرایش کارت بانکی، تطبیق با سرویس احراز هویت جیبیت انجام میشود.',
+ register_jibit_verify:
+ 'روشن: در ثبتنام، تطبیق کد ملی و شماره موبایل از طریق جیبیت انجام میشود.',
+ has_page_earn_money:
+ 'روشن: منوی «کسب درآمد / دعوت دوستان» در اپ مشتری نمایش داده میشود.',
+ invite_link_text:
+ 'متن پیام اشتراکگذاری دعوت. میتوانید از ٪نام برای نام کاربر و ٪لینک برای لینک ثبتنام با کد معرف استفاده کنید. خالی = متن پیشفرض اپ.',
+ montzar_mobile:
+ 'شماره موبایل حساب اتصال به سرویس همکار قیمت.',
+ montzar_password:
+ 'رمز عبور حساب اتصال به سرویس همکار قیمت. محرمانه نگه دارید.',
+ montzar_product_id:
+ 'شناسه محصول مرجع در سرویس همکار قیمت.',
+ montzar_base_url:
+ 'آدرس پایه سرور سرویس همکار (بدون اسلش انتهایی).',
+ montzar_open_buy:
+ 'روشن: ثبت سفارش خرید از طریق قیمت همکار مجاز است (وضعیت واقعی از سرویس خارجی خوانده میشود).',
+ montzar_open_sell:
+ 'روشن: ثبت سفارش فروش از طریق قیمت همکار مجاز است.',
+ montzar_closed:
+ 'وضعیت گزارششده از سرویس همکار؛ فقط برای نمایش — این سوییچ را دستی تغییر ندهید.',
+ external_order_api_enabled:
+ 'روشن: دریافت کیف پول و ثبت سفارش از سرویس فروشگاه خارجی فعال است.',
+ node_client_id:
+ 'شناسه کلاینت برای اتصال امن به سرویس سفارش خارجی.',
+ node_shared_secret:
+ 'کلید مشترک امضای درخواستها به سرویس سفارش خارجی. محرمانه نگه دارید.',
+ support_messenger:
+ 'پیامرسانی که دکمه پشتیبانی در اپ مشتری به آن هدایت میکند.',
+ support_messenger_id:
+ 'شناسه تماس پشتیبانی: یوزرنیم، شماره یا آیدی بسته به پیامرسان انتخابشده.',
+ support_messenger_text:
+ 'متن کوتاه کنار دکمه پشتیبانی در اپ مشتری.',
+ deposit_sms_support_number:
+ 'شماره پشتیبانی واریز؛ در صفحه شارژ حساب دکمه پیامک با متن شماره حساب نشان داده میشود.',
+ charge_notif:
+ 'روشن: پس از شارژ کیف توسط ادمین، پیامک به مشتری ارسال میشود.',
+ group_sms:
+ 'روشن: امکان ارسال پیامک گروهی از پنل ادمین فعال است.',
+ profit_gold_api_enabled:
+ 'روشن: محاسبه سود طلا از API خارجی فعال است. در اکثر نصبها خاموش میماند.',
+ default_vc_number:
+ 'پیشوند اختیاری برای شمارهگذاری حوالهها در حسابداری خارجی. خالی = بدون پیشوند.',
+ unit:
+ 'واحد نمایش وزن در داراییها، کیف پول و جزئیات معاملات (گرم یا کیلوگرم).',
+ chart_use_local_data:
+ 'روشن: نمودار از تاریخچه ذخیرهشده در دیتابیس خودتان. خاموش: از سرویس تابلو آنلاین.',
+ chart_id:
+ 'شناسه نمودار در سرویس تابلو. خالی = شناسه پیشفرض سرویس.',
+ price_provider:
+ 'منبع دریافت قیمت لحظهای برای اپ مشتری و همگامسازی محصولات.',
+ main_product:
+ 'شناسه یا نام محصول مرجع آبشده. وقتی منبع قیمت «محصول مرجع» باشد برای محاسبه استفاده میشود.',
+ zaarbox_api_url:
+ 'آدرس پایه API سرویس تابلو آنلاین.',
+ zaarbox_price_key:
+ 'شناسه ردیف قیمت در پاسخ سرویس تابلو.',
+ payping_fin_sync_enabled:
+ 'روشن: پس از واریز یا برداشت، تراکنش به سامانه حسابداری مالی همگام میشود.',
+ payping_fin_url:
+ 'آدرس ثبت تراکنش در سامانه حسابداری مالی.',
+ payping_fin_token:
+ 'توکن احراز هویت سامانه حسابداری مالی.',
+ payping_fin_customer_id:
+ 'شناسه مشتری در سامانه حسابداری که تراکنشها به نام آن ثبت میشوند.',
+ payping_gateway_name:
+ 'نام درگاه پرداخت فعال که به واسط درگاه ارسال میشود.',
+ payping_gateway_item:
+ 'کد درگاه (تکی). اگر لیست چرخشی درگاه پر باشد، اولویت با آن است.',
+ payping_gateway_items:
+ 'لیست کدهای درگاه با ویرگول برای چرخش بین چند پذیرنده. خالی = استفاده از کد تکی.',
+ payping_start_url:
+ 'آدرس API شروع پرداخت (هدایت مشتری به درگاه).',
+ payping_verify_url:
+ 'آدرس API تأیید پرداخت پس از بازگشت از درگاه.',
+ payping_round_toman:
+ 'روشن: مبلغ پرداخت به نزدیکترین تومان گرد میشود. خاموش: مبلغ دقیق ریالی.',
+ payping_strict_failed_withdraw:
+ 'روشن: در صورت برداشت ناموفق قبلی، تسویه جدید با احتیاط بیشتر انجام میشود.',
+ send_to_tahesab:
+ 'روشن: واریز و برداشت مشتریان بهصورت خودکار به تهحساب ارسال میشود.',
+ tahesab_host:
+ 'آدرس سرور نرمافزار تهحساب در شبکه.',
+ tahesab_port:
+ 'شماره پورت اتصال به تهحساب.',
+ tahesab_token:
+ 'توکن اتصال به تهحساب. از پنل تهحساب دریافت میشود.',
+ tahesab_db:
+ 'نام پایگاه داده در تهحساب که سند به آن ارسال میشود.',
+ tahesab_bank_account:
+ 'نام یا کد حساب بانکی در سند واریز/برداشت تهحساب.',
+ montify_name:
+ 'نام فروشگاه که در درگاه پرداخت و رسید بانکی به مشتری نشان داده میشود.',
+ app_url:
+ 'آدرس کامل وباپ مشتری (با https). در پیامکها و بازگشت از درگاه استفاده میشود.',
+ admin_url:
+ 'آدرس کامل پنل مدیریت (با https).',
+ main_product_price:
+ 'تنظیم قدیمی — از «منبع قیمت» استفاده کنید. روشن نگه داشتن توصیه نمیشود.'
}
-/** برچسب فارسی ساده برای کلیدهای مهم — اگر سرور برچسبی نفرستد */
+/** برچسب فارسی ساده — اگر سرور برچسبی نفرستد یا برچسب فنی باشد */
export const KEY_LABEL_FALLBACK: Record = {
close_price: 'بستن خودکار خارج از ساعت کاری',
startDay: 'ساعت شروع',
endDay: 'ساعت پایان',
- show_last_price: 'نمایش آخرین قیمت ذخیرهشده'
+ show_last_price: 'نمایش آخرین قیمت ذخیرهشده',
+ fee_single: 'اختلاف پایه خرید/فروش',
+ buy_fee: 'اضافه قیمت خرید',
+ sell_fee: 'کسر قیمت فروش',
+ fee_service_charge_percentage: 'درصد کارمزد معامله',
+ fee_free: 'معافیت کارمزد سفارش اول',
+ min_withdraw: 'حداقل مبلغ برداشت',
+ min_first_buy: 'حداقل اولین خرید',
+ minimum_sell: 'حداقل مقدار فروش',
+ referral_id: 'کد معرف اجباری',
+ open_register: 'ثبتنام باز',
+ auto_active_customer: 'فعالسازی خودکار پس از ثبتنام',
+ balance_report_exclude_customer_id: 'حذف مشتری از گزارش تراز',
+ chart_use_local_data: 'نمودار از داده داخلی',
+ chart_id: 'شناسه نمودار تابلو',
+ price_provider: 'منبع قیمت',
+ main_product: 'محصول مرجع قیمت',
+ montzar_open_buy: 'خرید از همکار',
+ montzar_open_sell: 'فروش از همکار',
+ external_order_api_enabled: 'API سفارش خارجی',
+ payping_fin_sync_enabled: 'همگامسازی با حسابداری مالی',
+ send_to_tahesab: 'ارسال خودکار به تهحساب',
+ support_messenger: 'پیامرسان پشتیبانی',
+ deposit_sms_support_number: 'شماره پشتیبانی واریز'
}
export function helpText(row: Const): string {
+ const key = (row.key ?? '').trim()
+ if (key && KEY_HELP_FALLBACK[key]) {
+ return KEY_HELP_FALLBACK[key]
+ }
+
const desc = (row.description || '').trim()
if (!desc) {
- return (row.key && KEY_HELP_FALLBACK[row.key]) || ''
+ return ''
+ }
+ if (resolveSettingFieldType(row) === 'select' && desc.includes('|')) {
+ return desc.split('|')[0]?.trim() ?? ''
}
if (resolveSettingFieldType(row) === 'select' && desc.includes(',')) {
- const parts = desc.split('|')
- return parts.length > 1 ? parts[0].trim() : ''
+ return ''
}
return desc
}