mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 17:43:47 +00:00
feat: show partner campaign links with bonuses on referral page
Display campaigns assigned to the partner with shareable deep links, copy button, and bonus type badges (balance/subscription/tariff).
This commit is contained in:
@@ -16,10 +16,23 @@ export interface PartnerApplicationInfo {
|
|||||||
processed_at: string | null;
|
processed_at: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PartnerCampaignInfo {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
start_parameter: string;
|
||||||
|
bonus_type: string;
|
||||||
|
balance_bonus_kopeks: number;
|
||||||
|
subscription_duration_days: number | null;
|
||||||
|
subscription_traffic_gb: number | null;
|
||||||
|
deep_link: string | null;
|
||||||
|
web_link: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface PartnerStatusResponse {
|
export interface PartnerStatusResponse {
|
||||||
partner_status: string;
|
partner_status: string;
|
||||||
commission_percent: number | null;
|
commission_percent: number | null;
|
||||||
latest_application: PartnerApplicationInfo | null;
|
latest_application: PartnerApplicationInfo | null;
|
||||||
|
campaigns: PartnerCampaignInfo[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PartnerApplicationRequest {
|
export interface PartnerApplicationRequest {
|
||||||
|
|||||||
@@ -670,6 +670,12 @@
|
|||||||
"applying": "Submitting...",
|
"applying": "Submitting...",
|
||||||
"submitApplication": "Submit Application",
|
"submitApplication": "Submit Application",
|
||||||
"applyError": "Failed to submit application. Please try again.",
|
"applyError": "Failed to submit application. Please try again.",
|
||||||
|
"yourCampaigns": "Your campaigns",
|
||||||
|
"campaignBonus": {
|
||||||
|
"balance": "+{{amount}} to balance",
|
||||||
|
"subscription": "{{days}} days subscription",
|
||||||
|
"tariff": "Bonus tariff"
|
||||||
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"companyName": "Company Name",
|
"companyName": "Company Name",
|
||||||
"companyNamePlaceholder": "Your company or brand name",
|
"companyNamePlaceholder": "Your company or brand name",
|
||||||
|
|||||||
@@ -561,6 +561,12 @@
|
|||||||
"applying": "در حال ارسال...",
|
"applying": "در حال ارسال...",
|
||||||
"submitApplication": "ارسال درخواست",
|
"submitApplication": "ارسال درخواست",
|
||||||
"applyError": "ارسال درخواست ناموفق بود. لطفاً دوباره تلاش کنید.",
|
"applyError": "ارسال درخواست ناموفق بود. لطفاً دوباره تلاش کنید.",
|
||||||
|
"yourCampaigns": "کمپینهای شما",
|
||||||
|
"campaignBonus": {
|
||||||
|
"balance": "+{{amount}} به موجودی",
|
||||||
|
"subscription": "{{days}} روز اشتراک",
|
||||||
|
"tariff": "تعرفه جایزه"
|
||||||
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"companyName": "نام شرکت",
|
"companyName": "نام شرکت",
|
||||||
"companyNamePlaceholder": "نام شرکت یا برند شما",
|
"companyNamePlaceholder": "نام شرکت یا برند شما",
|
||||||
|
|||||||
@@ -697,6 +697,12 @@
|
|||||||
"applying": "Отправка...",
|
"applying": "Отправка...",
|
||||||
"submitApplication": "Отправить заявку",
|
"submitApplication": "Отправить заявку",
|
||||||
"applyError": "Не удалось отправить заявку. Попробуйте снова.",
|
"applyError": "Не удалось отправить заявку. Попробуйте снова.",
|
||||||
|
"yourCampaigns": "Ваши кампании",
|
||||||
|
"campaignBonus": {
|
||||||
|
"balance": "+{{amount}} на баланс",
|
||||||
|
"subscription": "{{days}} дн. подписки",
|
||||||
|
"tariff": "Бонусный тариф"
|
||||||
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"companyName": "Название компании",
|
"companyName": "Название компании",
|
||||||
"companyNamePlaceholder": "Название вашей компании или бренда",
|
"companyNamePlaceholder": "Название вашей компании или бренда",
|
||||||
|
|||||||
@@ -561,6 +561,12 @@
|
|||||||
"applying": "提交中...",
|
"applying": "提交中...",
|
||||||
"submitApplication": "提交申请",
|
"submitApplication": "提交申请",
|
||||||
"applyError": "提交申请失败,请重试。",
|
"applyError": "提交申请失败,请重试。",
|
||||||
|
"yourCampaigns": "您的活动",
|
||||||
|
"campaignBonus": {
|
||||||
|
"balance": "+{{amount}} 余额",
|
||||||
|
"subscription": "{{days}} 天订阅",
|
||||||
|
"tariff": "奖励套餐"
|
||||||
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"companyName": "公司名称",
|
"companyName": "公司名称",
|
||||||
"companyNamePlaceholder": "您的公司或品牌名称",
|
"companyNamePlaceholder": "您的公司或品牌名称",
|
||||||
|
|||||||
@@ -8,6 +8,16 @@ import { partnerApi } from '../api/partners';
|
|||||||
import { withdrawalApi } from '../api/withdrawals';
|
import { withdrawalApi } from '../api/withdrawals';
|
||||||
import { useCurrency } from '../hooks/useCurrency';
|
import { useCurrency } from '../hooks/useCurrency';
|
||||||
|
|
||||||
|
const LinkIcon = () => (
|
||||||
|
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
const CopyIcon = () => (
|
const CopyIcon = () => (
|
||||||
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||||
<path
|
<path
|
||||||
@@ -83,6 +93,7 @@ export default function Referral() {
|
|||||||
const { formatAmount, currencySymbol, formatPositive, formatWithCurrency } = useCurrency();
|
const { formatAmount, currencySymbol, formatPositive, formatWithCurrency } = useCurrency();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
|
const [copiedCampaignId, setCopiedCampaignId] = useState<number | null>(null);
|
||||||
|
|
||||||
const { data: info, isLoading } = useQuery({
|
const { data: info, isLoading } = useQuery({
|
||||||
queryKey: ['referral-info'],
|
queryKey: ['referral-info'],
|
||||||
@@ -508,6 +519,85 @@ export default function Referral() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* ==================== Partner Campaigns Section ==================== */}
|
||||||
|
|
||||||
|
{isPartner && partnerStatus?.campaigns && partnerStatus.campaigns.length > 0 && (
|
||||||
|
<div className="bento-card">
|
||||||
|
<div className="mb-4 flex items-center gap-3">
|
||||||
|
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-accent-500/10 text-accent-400">
|
||||||
|
<LinkIcon />
|
||||||
|
</div>
|
||||||
|
<h2 className="text-lg font-semibold text-dark-100">
|
||||||
|
{t('referral.partner.yourCampaigns')}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-3">
|
||||||
|
{partnerStatus.campaigns.map((campaign) => {
|
||||||
|
const shareUrl = campaign.deep_link || campaign.web_link || '';
|
||||||
|
const isCopied = copiedCampaignId === campaign.id;
|
||||||
|
|
||||||
|
const copyCampaignLink = () => {
|
||||||
|
if (!shareUrl) return;
|
||||||
|
navigator.clipboard.writeText(shareUrl);
|
||||||
|
setCopiedCampaignId(campaign.id);
|
||||||
|
setTimeout(() => setCopiedCampaignId(null), 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const bonusBadge = (() => {
|
||||||
|
switch (campaign.bonus_type) {
|
||||||
|
case 'balance':
|
||||||
|
return {
|
||||||
|
label: t('referral.partner.campaignBonus.balance', {
|
||||||
|
amount: formatWithCurrency(campaign.balance_bonus_kopeks / 100, 0),
|
||||||
|
}),
|
||||||
|
className: 'badge-success',
|
||||||
|
};
|
||||||
|
case 'subscription':
|
||||||
|
return {
|
||||||
|
label: t('referral.partner.campaignBonus.subscription', {
|
||||||
|
days: campaign.subscription_duration_days ?? 0,
|
||||||
|
}),
|
||||||
|
className: 'badge-info',
|
||||||
|
};
|
||||||
|
case 'tariff':
|
||||||
|
return {
|
||||||
|
label: t('referral.partner.campaignBonus.tariff'),
|
||||||
|
className: 'badge-info',
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={campaign.id}
|
||||||
|
className="rounded-xl border border-dark-700/30 bg-dark-800/30 p-4"
|
||||||
|
>
|
||||||
|
<div className="mb-2 flex items-center gap-2">
|
||||||
|
<span className="font-medium text-dark-100">{campaign.name}</span>
|
||||||
|
{bonusBadge && <span className={bonusBadge.className}>{bonusBadge.label}</span>}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<input type="text" readOnly value={shareUrl} className="input flex-1 text-xs" />
|
||||||
|
<button
|
||||||
|
onClick={copyCampaignLink}
|
||||||
|
disabled={!shareUrl}
|
||||||
|
className={`btn-primary shrink-0 px-3 py-2 ${
|
||||||
|
isCopied ? 'bg-success-500 hover:bg-success-500' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{isCopied ? <CheckIcon /> : <CopyIcon />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* ==================== Withdrawal Section (approved partners only) ==================== */}
|
{/* ==================== Withdrawal Section (approved partners only) ==================== */}
|
||||||
|
|
||||||
{isPartner && (
|
{isPartner && (
|
||||||
|
|||||||
Reference in New Issue
Block a user