mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-30 02:23:47 +00:00
feat: admin partner settings page, partner section visibility toggle, custom requisites text
- New AdminPartnerSettings page with validation (pattern from AdminTicketSettings) - Settings gear button on AdminPartners header - Hide partner/withdrawal sections when partner_section_visible is false - Use custom requisites_text as label in withdrawal form - i18n keys for ru/en
This commit is contained in:
@@ -403,7 +403,7 @@ export default function Referral() {
|
||||
{/* ==================== Partner Application Section ==================== */}
|
||||
|
||||
{/* Status: none — Become a Partner CTA */}
|
||||
{showApplySection && (
|
||||
{terms?.partner_section_visible !== false && showApplySection && (
|
||||
<div className="bento-card">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex h-14 w-14 shrink-0 items-center justify-center rounded-2xl bg-accent-500/10 text-accent-400">
|
||||
@@ -428,7 +428,7 @@ export default function Referral() {
|
||||
)}
|
||||
|
||||
{/* Status: pending — Application Under Review */}
|
||||
{showPendingSection && (
|
||||
{terms?.partner_section_visible !== false && showPendingSection && (
|
||||
<div className="bento-card border-warning-500/20">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex h-14 w-14 shrink-0 items-center justify-center rounded-2xl bg-warning-500/10 text-warning-400">
|
||||
@@ -454,7 +454,7 @@ export default function Referral() {
|
||||
)}
|
||||
|
||||
{/* Status: approved — Partner Badge */}
|
||||
{showApprovedSection && (
|
||||
{terms?.partner_section_visible !== false && showApprovedSection && (
|
||||
<div className="bento-card border-success-500/20">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex h-14 w-14 shrink-0 items-center justify-center rounded-2xl bg-success-500/10 text-success-400">
|
||||
@@ -481,7 +481,7 @@ export default function Referral() {
|
||||
)}
|
||||
|
||||
{/* Status: rejected — Rejection Notice */}
|
||||
{showRejectedSection && (
|
||||
{terms?.partner_section_visible !== false && showRejectedSection && (
|
||||
<div className="bento-card border-error-500/20">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex h-14 w-14 shrink-0 items-center justify-center rounded-2xl bg-error-500/10 text-error-400">
|
||||
@@ -521,116 +521,119 @@ export default function Referral() {
|
||||
|
||||
{/* ==================== Partner Campaigns Section ==================== */}
|
||||
|
||||
{isPartner && partnerStatus?.campaigns && partnerStatus.campaigns.length > 0 && (
|
||||
<div className="space-y-4">
|
||||
<div className="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>
|
||||
|
||||
{partnerStatus.campaigns.map((campaign) => {
|
||||
const copyLink = (url: string, key: string) => {
|
||||
navigator.clipboard.writeText(url);
|
||||
setCopiedLink(key);
|
||||
setTimeout(() => setCopiedLink(null), 2000);
|
||||
};
|
||||
|
||||
const botKey = `${campaign.id}-bot`;
|
||||
const webKey = `${campaign.id}-web`;
|
||||
|
||||
return (
|
||||
<div key={campaign.id} className="bento-card space-y-4">
|
||||
{/* Campaign header */}
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="text-base font-semibold text-dark-100">{campaign.name}</h3>
|
||||
</div>
|
||||
|
||||
{/* Bonus info */}
|
||||
{campaign.bonus_type !== 'none' && (
|
||||
<div className="rounded-lg bg-success-500/10 p-3">
|
||||
<div className="mb-1 text-xs font-medium text-success-500">
|
||||
{t('referral.partner.campaignBonus.title')}
|
||||
</div>
|
||||
<div className="text-sm font-semibold text-success-400">
|
||||
{campaign.bonus_type === 'balance' &&
|
||||
t('referral.partner.campaignBonus.balanceDesc', {
|
||||
amount: formatWithCurrency(campaign.balance_bonus_kopeks / 100, 0),
|
||||
})}
|
||||
{campaign.bonus_type === 'subscription' &&
|
||||
t('referral.partner.campaignBonus.subscriptionDesc', {
|
||||
days: campaign.subscription_duration_days ?? 0,
|
||||
...(campaign.subscription_traffic_gb
|
||||
? { traffic: campaign.subscription_traffic_gb }
|
||||
: {}),
|
||||
})}
|
||||
{campaign.bonus_type === 'tariff' &&
|
||||
t('referral.partner.campaignBonus.tariffDesc')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Bot link */}
|
||||
{campaign.deep_link && (
|
||||
<div>
|
||||
<div className="mb-1 text-xs font-medium text-dark-500">
|
||||
{t('referral.partner.campaignLinks.bot')}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="text"
|
||||
readOnly
|
||||
value={campaign.deep_link}
|
||||
className="input flex-1 text-xs"
|
||||
/>
|
||||
<button
|
||||
onClick={() => copyLink(campaign.deep_link!, botKey)}
|
||||
className={`btn-primary shrink-0 px-3 py-2 ${
|
||||
copiedLink === botKey ? 'bg-success-500 hover:bg-success-500' : ''
|
||||
}`}
|
||||
>
|
||||
{copiedLink === botKey ? <CheckIcon /> : <CopyIcon />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Web link */}
|
||||
{campaign.web_link && (
|
||||
<div>
|
||||
<div className="mb-1 text-xs font-medium text-dark-500">
|
||||
{t('referral.partner.campaignLinks.web')}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="text"
|
||||
readOnly
|
||||
value={campaign.web_link}
|
||||
className="input flex-1 text-xs"
|
||||
/>
|
||||
<button
|
||||
onClick={() => copyLink(campaign.web_link!, webKey)}
|
||||
className={`btn-primary shrink-0 px-3 py-2 ${
|
||||
copiedLink === webKey ? 'bg-success-500 hover:bg-success-500' : ''
|
||||
}`}
|
||||
>
|
||||
{copiedLink === webKey ? <CheckIcon /> : <CopyIcon />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{terms?.partner_section_visible !== false &&
|
||||
isPartner &&
|
||||
partnerStatus?.campaigns &&
|
||||
partnerStatus.campaigns.length > 0 && (
|
||||
<div className="space-y-4">
|
||||
<div className="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>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<h2 className="text-lg font-semibold text-dark-100">
|
||||
{t('referral.partner.yourCampaigns')}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
{partnerStatus.campaigns.map((campaign) => {
|
||||
const copyLink = (url: string, key: string) => {
|
||||
navigator.clipboard.writeText(url);
|
||||
setCopiedLink(key);
|
||||
setTimeout(() => setCopiedLink(null), 2000);
|
||||
};
|
||||
|
||||
const botKey = `${campaign.id}-bot`;
|
||||
const webKey = `${campaign.id}-web`;
|
||||
|
||||
return (
|
||||
<div key={campaign.id} className="bento-card space-y-4">
|
||||
{/* Campaign header */}
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="text-base font-semibold text-dark-100">{campaign.name}</h3>
|
||||
</div>
|
||||
|
||||
{/* Bonus info */}
|
||||
{campaign.bonus_type !== 'none' && (
|
||||
<div className="rounded-lg bg-success-500/10 p-3">
|
||||
<div className="mb-1 text-xs font-medium text-success-500">
|
||||
{t('referral.partner.campaignBonus.title')}
|
||||
</div>
|
||||
<div className="text-sm font-semibold text-success-400">
|
||||
{campaign.bonus_type === 'balance' &&
|
||||
t('referral.partner.campaignBonus.balanceDesc', {
|
||||
amount: formatWithCurrency(campaign.balance_bonus_kopeks / 100, 0),
|
||||
})}
|
||||
{campaign.bonus_type === 'subscription' &&
|
||||
t('referral.partner.campaignBonus.subscriptionDesc', {
|
||||
days: campaign.subscription_duration_days ?? 0,
|
||||
...(campaign.subscription_traffic_gb
|
||||
? { traffic: campaign.subscription_traffic_gb }
|
||||
: {}),
|
||||
})}
|
||||
{campaign.bonus_type === 'tariff' &&
|
||||
t('referral.partner.campaignBonus.tariffDesc')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Bot link */}
|
||||
{campaign.deep_link && (
|
||||
<div>
|
||||
<div className="mb-1 text-xs font-medium text-dark-500">
|
||||
{t('referral.partner.campaignLinks.bot')}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="text"
|
||||
readOnly
|
||||
value={campaign.deep_link}
|
||||
className="input flex-1 text-xs"
|
||||
/>
|
||||
<button
|
||||
onClick={() => copyLink(campaign.deep_link!, botKey)}
|
||||
className={`btn-primary shrink-0 px-3 py-2 ${
|
||||
copiedLink === botKey ? 'bg-success-500 hover:bg-success-500' : ''
|
||||
}`}
|
||||
>
|
||||
{copiedLink === botKey ? <CheckIcon /> : <CopyIcon />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Web link */}
|
||||
{campaign.web_link && (
|
||||
<div>
|
||||
<div className="mb-1 text-xs font-medium text-dark-500">
|
||||
{t('referral.partner.campaignLinks.web')}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="text"
|
||||
readOnly
|
||||
value={campaign.web_link}
|
||||
className="input flex-1 text-xs"
|
||||
/>
|
||||
<button
|
||||
onClick={() => copyLink(campaign.web_link!, webKey)}
|
||||
className={`btn-primary shrink-0 px-3 py-2 ${
|
||||
copiedLink === webKey ? 'bg-success-500 hover:bg-success-500' : ''
|
||||
}`}
|
||||
>
|
||||
{copiedLink === webKey ? <CheckIcon /> : <CopyIcon />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ==================== Withdrawal Section (approved partners only) ==================== */}
|
||||
|
||||
{isPartner && (
|
||||
{terms?.partner_section_visible !== false && isPartner && (
|
||||
<div id="withdrawal-section" className="space-y-6">
|
||||
{/* Withdrawal Balance Card */}
|
||||
{withdrawalBalance && (
|
||||
|
||||
Reference in New Issue
Block a user