This commit is contained in:
2026-06-30 22:19:38 +03:30
parent 0e75f93836
commit 6c393ecb94
3 changed files with 199 additions and 15 deletions
+14
View File
@@ -0,0 +1,14 @@
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
#RewriteBase /
RewriteRule ^ /admin/index.html [L]
</IfModule>
@@ -82,7 +82,7 @@ export const EDITABLE_SETTING_GROUPS: SettingGroupDef[] = [
id: 'features', id: 'features',
title: 'قابلیت‌های سیستم', title: 'قابلیت‌های سیستم',
icon: 'vuesax-linear:setting-2', 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', id: 'misc',
+184 -14
View File
@@ -6,18 +6,17 @@ export type SettingFieldType = 'text' | 'number' | 'price' | 'switch' | 'select'
/** کلیدهای legacy — در پنل نمایش داده نمی‌شوند */ /** کلیدهای legacy — در پنل نمایش داده نمی‌شوند */
export const HIDDEN_LEGACY_SETTING_KEYS = new Set([ export const HIDDEN_LEGACY_SETTING_KEYS = new Set([
'main_product_price', 'main_product_price',
'montzar_price',
'etehadie_api_url', 'etehadie_api_url',
'goldir_price', 'goldir_price',
'price_primary_source', 'price_primary_source',
'price_method', 'price_method',
'montzar_last_product_log', 'montzar_last_product_log',
'montzar_order_response', 'montzar_order_response',
'service_charge_percentage' 'service_charge_percentage',
]) ])
/** فقط وقتی is_store فعال است در پنل دیده می‌شوند */ /** فقط وقتی 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 { export function isSettingLocked(canEdit: Const['can_edit']): boolean {
return canEdit === 0 || canEdit === false || canEdit === '0' return canEdit === 0 || canEdit === false || canEdit === '0'
@@ -90,7 +89,6 @@ const KEY_TYPE_FALLBACK: Record<string, SettingFieldType> = {
payping_fin_sync_enabled: 'switch', payping_fin_sync_enabled: 'switch',
external_order_api_enabled: 'switch', external_order_api_enabled: 'switch',
is_store: 'switch', is_store: 'switch',
price_from_nasiryan: 'switch',
banner_enabled: 'switch', banner_enabled: 'switch',
apk_download_link: 'text', apk_download_link: 'text',
node_client_id: 'text', node_client_id: 'text',
@@ -224,34 +222,206 @@ export function switchLabels(key: string | undefined): { on: string; off: string
return { on: 'فعال', off: 'غیرفعال' } return { on: 'فعال', off: 'غیرفعال' }
} }
/** توضیحات کامل و ساده برای کلیدهای مهم — اگر description در دیتابیس خالی بود استفاده می‌شود */ /** توضیحات ساده و فارسی برای پنل — بر توضیح دیتابیس (که ممکن است نام فنی انگلیسی داشته باشد) اولویت دارد */
const KEY_HELP_FALLBACK: Record<string, string> = { const KEY_HELP_FALLBACK: Record<string, string> = {
name_fa:
'نام فارسی برند که در پیامک‌ها، عنوان صفحات و بخش‌های عمومی اپ به مشتری نشان داده می‌شود.',
open_register:
'روشن: کاربران جدید می‌توانند ثبت‌نام کنند. خاموش: فقط ورود کاربران قبلی مجاز است.',
auto_active_customer:
'روشن: حساب بلافاصله پس از ثبت‌نام فعال می‌شود. خاموش: نیاز به تأیید دستی شما در پنل دارد.',
close_price: close_price:
'فیلتر اصلی فروشگاه. وقتی روشن باشد، خرید و فروش و نمایش قیمت فقط در بازهٔ «ساعت شروع» تا «ساعت پایان» انجام می‌شود و خارج از این بازه به مشتری پیام «فروشگاه بسته است» نشان داده می‌شود. اگر خاموش باشد، فروشگاه همیشه باز است (مگر اینکه برای محصولی به‌صورت اختصاصی ساعت تعریف کرده باشید).', 'فیلتر اصلی فروشگاه. وقتی روشن باشد، خرید و فروش و نمایش قیمت فقط بین «ساعت شروع» و «ساعت پایان» انجام می‌شود و خارج از این بازه به مشتری «فروشگاه بسته است» نشان داده می‌شود. خاموش: محدودیت ساعتی سراسری اعمال نمی‌شود (مگر برای محصولی ساعت اختصاصی تعریف کرده باشید).',
startDay: startDay:
'ساعت شروع فعالیت فروشگاه. فقط وقتی «بستن خودکار خارج از ساعت کاری» روشن باشد اعمال می‌شود. ساعت را از روی ساعت‌شمار انتخاب کنید (مثلاً ۰۹:۰۰).', 'ساعت شروع فعالیت روزانه فروشگاه. فقط وقتی «بستن خودکار خارج از ساعت کاری» روشن باشد اعمال می‌شود. از ساعت‌شمار انتخاب کنید (مثلاً ۰۹:۰۰).',
endDay: 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:
'حداقل مقدار یا مبلغ مجاز برای ثبت سفارش فروش.',
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<string, string> = { export const KEY_LABEL_FALLBACK: Record<string, string> = {
close_price: 'بستن خودکار خارج از ساعت کاری', close_price: 'بستن خودکار خارج از ساعت کاری',
startDay: 'ساعت شروع', startDay: 'ساعت شروع',
endDay: 'ساعت پایان', 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 { 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() const desc = (row.description || '').trim()
if (!desc) { 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(',')) { if (resolveSettingFieldType(row) === 'select' && desc.includes(',')) {
const parts = desc.split('|') return ''
return parts.length > 1 ? parts[0].trim() : ''
} }
return desc return desc
} }