Files
bedolaga-cabinet/src/pages/ReferralPartnerApply.tsx
Fringg e94d81fe5a fix: partner system bugs - commission field, withdrawal UX, admin amount
1. Add desired commission percent field to partner application form
   and admin review page (Bug 1)
2. Add prominent "Requested Amount" row in admin withdrawal detail (Bug 2)
3. Switch withdrawal input from kopecks to rubles with auto-conversion
   on submit (Bug 3)

Includes i18n updates for all 4 locales (ru, en, zh, fa).
2026-02-27 04:02:27 +03:00

182 lines
6.5 KiB
TypeScript

import { useState, useEffect, type FormEvent } from 'react';
import { useNavigate } from 'react-router';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { partnerApi, type PartnerApplicationRequest } from '../api/partners';
export default function ReferralPartnerApply() {
const { t } = useTranslation();
const navigate = useNavigate();
const queryClient = useQueryClient();
const [form, setForm] = useState<PartnerApplicationRequest>({
company_name: '',
website_url: '',
telegram_channel: '',
description: '',
expected_monthly_referrals: undefined,
desired_commission_percent: undefined,
});
// Guard: redirect if already approved or pending
const { data: partnerStatus } = useQuery({
queryKey: ['partner-status'],
queryFn: partnerApi.getStatus,
});
useEffect(() => {
if (
partnerStatus?.partner_status === 'approved' ||
partnerStatus?.partner_status === 'pending'
) {
navigate('/referral', { replace: true });
}
}, [partnerStatus, navigate]);
const applyMutation = useMutation({
mutationFn: partnerApi.apply,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['partner-status'] });
navigate('/referral');
},
});
const handleSubmit = (e: FormEvent) => {
e.preventDefault();
const payload: PartnerApplicationRequest = {};
if (form.company_name) payload.company_name = form.company_name;
if (form.website_url) payload.website_url = form.website_url;
if (form.telegram_channel) payload.telegram_channel = form.telegram_channel;
if (form.description) payload.description = form.description;
if (form.expected_monthly_referrals) {
payload.expected_monthly_referrals = form.expected_monthly_referrals;
}
if (form.desired_commission_percent) {
payload.desired_commission_percent = form.desired_commission_percent;
}
applyMutation.mutate(payload);
};
return (
<div className="animate-fade-in space-y-6">
<h1 className="text-2xl font-bold text-dark-50">{t('referral.partner.applyTitle')}</h1>
<p className="text-sm text-dark-400">{t('referral.partner.applyDesc')}</p>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="bento-card space-y-4">
<div>
<label className="mb-1 block text-sm font-medium text-dark-300">
{t('referral.partner.fields.companyName')}
</label>
<input
type="text"
className="input w-full"
value={form.company_name ?? ''}
onChange={(e) => setForm({ ...form, company_name: e.target.value })}
placeholder={t('referral.partner.fields.companyNamePlaceholder')}
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-dark-300">
{t('referral.partner.fields.telegramChannel')}
</label>
<input
type="text"
className="input w-full"
value={form.telegram_channel ?? ''}
onChange={(e) => setForm({ ...form, telegram_channel: e.target.value })}
placeholder={t('referral.partner.fields.telegramChannelPlaceholder')}
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-dark-300">
{t('referral.partner.fields.websiteUrl')}
</label>
<input
type="url"
className="input w-full"
value={form.website_url ?? ''}
onChange={(e) => setForm({ ...form, website_url: e.target.value })}
placeholder={t('referral.partner.fields.websiteUrlPlaceholder')}
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-dark-300">
{t('referral.partner.fields.description')}
</label>
<textarea
className="input min-h-[80px] w-full"
value={form.description ?? ''}
onChange={(e) => setForm({ ...form, description: e.target.value })}
placeholder={t('referral.partner.fields.descriptionPlaceholder')}
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-dark-300">
{t('referral.partner.fields.expectedReferrals')}
</label>
<input
type="number"
min={0}
max={2000000000}
className="input w-full"
value={form.expected_monthly_referrals ?? ''}
onChange={(e) =>
setForm({
...form,
expected_monthly_referrals: e.target.value ? Number(e.target.value) : undefined,
})
}
placeholder={t('referral.partner.fields.expectedReferralsPlaceholder')}
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-dark-300">
{t('referral.partner.fields.desiredCommission')}
</label>
<input
type="number"
min={1}
max={100}
className="input w-full"
value={form.desired_commission_percent ?? ''}
onChange={(e) =>
setForm({
...form,
desired_commission_percent: e.target.value ? Number(e.target.value) : undefined,
})
}
placeholder={t('referral.partner.fields.desiredCommissionPlaceholder')}
/>
</div>
</div>
{applyMutation.isError && (
<div className="rounded-lg bg-error-500/10 p-3 text-sm text-error-400">
{t('referral.partner.applyError')}
</div>
)}
<div className="flex gap-3">
<button
type="button"
onClick={() => navigate('/referral')}
className="btn-secondary flex-1 px-5"
>
{t('common.cancel')}
</button>
<button
type="submit"
disabled={applyMutation.isPending}
className={`btn-primary flex-1 px-5 ${applyMutation.isPending ? 'opacity-50' : ''}`}
>
{applyMutation.isPending
? t('referral.partner.applying')
: t('referral.partner.submitApplication')}
</button>
</div>
</form>
</div>
);
}