From 92d206f5b655cca2cceff172305f07d5edc551b7 Mon Sep 17 00:00:00 2001 From: Fringg Date: Sun, 8 Feb 2026 20:29:56 +0300 Subject: [PATCH] feat: add inline referral commission editing in admin user card Admins can now edit individual referral commission percent directly in the user detail card. Shows "Default" when no custom value is set. --- src/api/adminUsers.ts | 11 +++++ src/locales/en.json | 4 +- src/locales/fa.json | 4 +- src/locales/ru.json | 4 +- src/locales/zh.json | 4 +- src/pages/AdminUserDetail.tsx | 78 +++++++++++++++++++++++++++++++---- 6 files changed, 93 insertions(+), 12 deletions(-) diff --git a/src/api/adminUsers.ts b/src/api/adminUsers.ts index e8f90b6..60f4cfe 100644 --- a/src/api/adminUsers.ts +++ b/src/api/adminUsers.ts @@ -447,6 +447,17 @@ export const adminUsersApi = { return response.data; }, + // Update referral commission + updateReferralCommission: async ( + userId: number, + commissionPercent: number | null, + ): Promise<{ success: boolean; message: string }> => { + const response = await apiClient.post(`/cabinet/admin/users/${userId}/referral-commission`, { + commission_percent: commissionPercent, + }); + return response.data; + }, + // Delete user (soft delete, does NOT remove from Remnawave) deleteUser: async ( userId: number, diff --git a/src/locales/en.json b/src/locales/en.json index f8e5d23..cb4d7dd 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1913,7 +1913,9 @@ "title": "Referral program", "referrals": "Referrals", "earned": "Earned", - "commission": "Commission" + "commission": "Commission", + "default": "Default", + "invalidPercent": "Commission must be between 0 and 100" }, "restrictions": { "title": "Restrictions", diff --git a/src/locales/fa.json b/src/locales/fa.json index 8d2eb68..a6cfb9f 100644 --- a/src/locales/fa.json +++ b/src/locales/fa.json @@ -1616,7 +1616,9 @@ "title": "برنامه ارجاع", "referrals": "ارجاعات", "earned": "درآمد", - "commission": "کمیسیون" + "commission": "کمیسیون", + "default": "پیش‌فرض", + "invalidPercent": "کمیسیون باید بین ۰ تا ۱۰۰ باشد" }, "restrictions": { "title": "محدودیت‌ها", diff --git a/src/locales/ru.json b/src/locales/ru.json index 91fb441..edc6d87 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -2439,7 +2439,9 @@ "title": "Реферальная программа", "referrals": "Рефералов", "earned": "Заработано", - "commission": "Комиссия" + "commission": "Комиссия", + "default": "По умолч.", + "invalidPercent": "Процент должен быть от 0 до 100" }, "restrictions": { "title": "Ограничения", diff --git a/src/locales/zh.json b/src/locales/zh.json index a5c9c11..38e20ce 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -1615,7 +1615,9 @@ "title": "推荐计划", "referrals": "推荐人数", "earned": "已赚取", - "commission": "佣金" + "commission": "佣金", + "default": "默认", + "invalidPercent": "佣金必须在0到100之间" }, "restrictions": { "title": "限制", diff --git a/src/pages/AdminUserDetail.tsx b/src/pages/AdminUserDetail.tsx index 9e9fbdc..a93611d 100644 --- a/src/pages/AdminUserDetail.tsx +++ b/src/pages/AdminUserDetail.tsx @@ -186,6 +186,10 @@ export default function AdminUserDetail() { const [promoGroups, setPromoGroups] = useState([]); const [editingPromoGroup, setEditingPromoGroup] = useState(false); + // Referral commission + const [editingReferralCommission, setEditingReferralCommission] = useState(false); + const [referralCommissionValue, setReferralCommissionValue] = useState(''); + // Send promo offer const [offerDiscountPercent, setOfferDiscountPercent] = useState(''); const [offerValidHours, setOfferValidHours] = useState(24); @@ -503,6 +507,25 @@ export default function AdminUserDetail() { } }; + const handleUpdateReferralCommission = async () => { + if (!userId) return; + setActionLoading(true); + try { + const value = referralCommissionValue === '' ? null : toNumber(referralCommissionValue); + if (value !== null && (value < 0 || value > 100)) { + notify.error(t('admin.users.detail.referral.invalidPercent'), t('common.error')); + return; + } + await adminUsersApi.updateReferralCommission(userId, value); + await loadUser(); + setEditingReferralCommission(false); + } catch { + notify.error(t('admin.users.userActions.error'), t('common.error')); + } finally { + setActionLoading(false); + } + }; + const handleDeactivateOffer = async () => { if (!userId) return; setActionLoading(true); @@ -850,8 +873,21 @@ export default function AdminUserDetail() { {/* Referral */}
-
- {t('admin.users.detail.referral.title')} +
+ + {t('admin.users.detail.referral.title')} + +
@@ -871,12 +907,38 @@ export default function AdminUserDetail() {
-
- {user.referral.commission_percent || 0}% -
-
- {t('admin.users.detail.referral.commission')} -
+ {editingReferralCommission ? ( +
+ + +
+ ) : ( + <> +
+ {user.referral.commission_percent != null + ? `${user.referral.commission_percent}%` + : t('admin.users.detail.referral.default')} +
+
+ {t('admin.users.detail.referral.commission')} +
+ + )}