mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 17:43:47 +00:00
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.
This commit is contained in:
@@ -447,6 +447,17 @@ export const adminUsersApi = {
|
|||||||
return response.data;
|
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)
|
// Delete user (soft delete, does NOT remove from Remnawave)
|
||||||
deleteUser: async (
|
deleteUser: async (
|
||||||
userId: number,
|
userId: number,
|
||||||
|
|||||||
@@ -1913,7 +1913,9 @@
|
|||||||
"title": "Referral program",
|
"title": "Referral program",
|
||||||
"referrals": "Referrals",
|
"referrals": "Referrals",
|
||||||
"earned": "Earned",
|
"earned": "Earned",
|
||||||
"commission": "Commission"
|
"commission": "Commission",
|
||||||
|
"default": "Default",
|
||||||
|
"invalidPercent": "Commission must be between 0 and 100"
|
||||||
},
|
},
|
||||||
"restrictions": {
|
"restrictions": {
|
||||||
"title": "Restrictions",
|
"title": "Restrictions",
|
||||||
|
|||||||
@@ -1616,7 +1616,9 @@
|
|||||||
"title": "برنامه ارجاع",
|
"title": "برنامه ارجاع",
|
||||||
"referrals": "ارجاعات",
|
"referrals": "ارجاعات",
|
||||||
"earned": "درآمد",
|
"earned": "درآمد",
|
||||||
"commission": "کمیسیون"
|
"commission": "کمیسیون",
|
||||||
|
"default": "پیشفرض",
|
||||||
|
"invalidPercent": "کمیسیون باید بین ۰ تا ۱۰۰ باشد"
|
||||||
},
|
},
|
||||||
"restrictions": {
|
"restrictions": {
|
||||||
"title": "محدودیتها",
|
"title": "محدودیتها",
|
||||||
|
|||||||
@@ -2439,7 +2439,9 @@
|
|||||||
"title": "Реферальная программа",
|
"title": "Реферальная программа",
|
||||||
"referrals": "Рефералов",
|
"referrals": "Рефералов",
|
||||||
"earned": "Заработано",
|
"earned": "Заработано",
|
||||||
"commission": "Комиссия"
|
"commission": "Комиссия",
|
||||||
|
"default": "По умолч.",
|
||||||
|
"invalidPercent": "Процент должен быть от 0 до 100"
|
||||||
},
|
},
|
||||||
"restrictions": {
|
"restrictions": {
|
||||||
"title": "Ограничения",
|
"title": "Ограничения",
|
||||||
|
|||||||
@@ -1615,7 +1615,9 @@
|
|||||||
"title": "推荐计划",
|
"title": "推荐计划",
|
||||||
"referrals": "推荐人数",
|
"referrals": "推荐人数",
|
||||||
"earned": "已赚取",
|
"earned": "已赚取",
|
||||||
"commission": "佣金"
|
"commission": "佣金",
|
||||||
|
"default": "默认",
|
||||||
|
"invalidPercent": "佣金必须在0到100之间"
|
||||||
},
|
},
|
||||||
"restrictions": {
|
"restrictions": {
|
||||||
"title": "限制",
|
"title": "限制",
|
||||||
|
|||||||
@@ -186,6 +186,10 @@ export default function AdminUserDetail() {
|
|||||||
const [promoGroups, setPromoGroups] = useState<PromoGroup[]>([]);
|
const [promoGroups, setPromoGroups] = useState<PromoGroup[]>([]);
|
||||||
const [editingPromoGroup, setEditingPromoGroup] = useState(false);
|
const [editingPromoGroup, setEditingPromoGroup] = useState(false);
|
||||||
|
|
||||||
|
// Referral commission
|
||||||
|
const [editingReferralCommission, setEditingReferralCommission] = useState(false);
|
||||||
|
const [referralCommissionValue, setReferralCommissionValue] = useState<number | ''>('');
|
||||||
|
|
||||||
// Send promo offer
|
// Send promo offer
|
||||||
const [offerDiscountPercent, setOfferDiscountPercent] = useState<number | ''>('');
|
const [offerDiscountPercent, setOfferDiscountPercent] = useState<number | ''>('');
|
||||||
const [offerValidHours, setOfferValidHours] = useState<number | ''>(24);
|
const [offerValidHours, setOfferValidHours] = useState<number | ''>(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 () => {
|
const handleDeactivateOffer = async () => {
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
setActionLoading(true);
|
setActionLoading(true);
|
||||||
@@ -850,8 +873,21 @@ export default function AdminUserDetail() {
|
|||||||
|
|
||||||
{/* Referral */}
|
{/* Referral */}
|
||||||
<div className="rounded-xl bg-dark-800/50 p-3">
|
<div className="rounded-xl bg-dark-800/50 p-3">
|
||||||
<div className="mb-2 text-sm font-medium text-dark-200">
|
<div className="mb-2 flex items-center justify-between">
|
||||||
{t('admin.users.detail.referral.title')}
|
<span className="text-sm font-medium text-dark-200">
|
||||||
|
{t('admin.users.detail.referral.title')}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
if (!editingReferralCommission) {
|
||||||
|
setReferralCommissionValue(user.referral.commission_percent ?? '');
|
||||||
|
}
|
||||||
|
setEditingReferralCommission(!editingReferralCommission);
|
||||||
|
}}
|
||||||
|
className="text-xs text-accent-400 transition-colors hover:text-accent-300"
|
||||||
|
>
|
||||||
|
{editingReferralCommission ? t('common.cancel') : t('common.edit')}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-3 gap-3 text-center">
|
<div className="grid grid-cols-3 gap-3 text-center">
|
||||||
<div>
|
<div>
|
||||||
@@ -871,12 +907,38 @@ export default function AdminUserDetail() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-lg font-bold text-dark-100">
|
{editingReferralCommission ? (
|
||||||
{user.referral.commission_percent || 0}%
|
<div className="space-y-1">
|
||||||
</div>
|
<input
|
||||||
<div className="text-xs text-dark-500">
|
type="number"
|
||||||
{t('admin.users.detail.referral.commission')}
|
value={referralCommissionValue}
|
||||||
</div>
|
onChange={createNumberInputHandler(setReferralCommissionValue, 0)}
|
||||||
|
placeholder="0-100"
|
||||||
|
className="input w-full text-center text-sm"
|
||||||
|
min={0}
|
||||||
|
max={100}
|
||||||
|
disabled={actionLoading}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={handleUpdateReferralCommission}
|
||||||
|
disabled={actionLoading}
|
||||||
|
className="w-full rounded-lg bg-accent-500 px-2 py-1 text-xs text-white transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{actionLoading ? t('common.loading') : t('common.save')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="text-lg font-bold text-dark-100">
|
||||||
|
{user.referral.commission_percent != null
|
||||||
|
? `${user.referral.commission_percent}%`
|
||||||
|
: t('admin.users.detail.referral.default')}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-dark-500">
|
||||||
|
{t('admin.users.detail.referral.commission')}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user