fix: fallback to referral role fill color when subscription_status is null

When subscription_status is not available (backend not deployed or user
has no subscription), fill color now falls through to referral role
(partner/topReferrer/activeReferrer) before defaulting to campaign/regular.
Increases border size from 0.25 to 0.35 and merges legend sections.
This commit is contained in:
Fringg
2026-03-22 09:32:53 +03:00
parent b18f3fb211
commit 61ca5cc53d
3 changed files with 18 additions and 24 deletions

View File

@@ -154,7 +154,12 @@ function buildFullGraph(graphData: NetworkGraphData): Graph {
});
graphData.users.forEach((user) => {
const fillColor = getNodeFillColor(user.subscription_status, user.campaign_id);
const fillColor = getNodeFillColor(
user.subscription_status,
user.campaign_id,
user.direct_referrals,
user.is_partner,
);
const borderColor = getNodeBorderColor(user.direct_referrals, user.is_partner);
const size = getUserNodeSize(user.direct_referrals);
const pos = positions.get(`user_${user.id}`) ?? {
@@ -176,7 +181,7 @@ function buildFullGraph(graphData: NetworkGraphData): Graph {
campaignId: user.campaign_id,
subscriptionStatus: user.subscription_status,
borderColor: borderColor ?? fillColor,
borderSize: borderColor ? 0.25 : 0,
borderSize: borderColor ? 0.35 : 0,
});
});

View File

@@ -18,14 +18,11 @@ const FILL_ITEMS = [
{ color: NODE_COLORS.trialActive, labelKey: 'admin.referralNetwork.legend.trialActive' },
{ color: NODE_COLORS.paidExpired, labelKey: 'admin.referralNetwork.legend.paidExpired' },
{ color: NODE_COLORS.trialExpired, labelKey: 'admin.referralNetwork.legend.trialExpired' },
{ color: NODE_COLORS.campaignUser, labelKey: 'admin.referralNetwork.legend.campaignUser' },
{ color: NODE_COLORS.regular, labelKey: 'admin.referralNetwork.legend.regularUser' },
];
const BORDER_ITEMS = [
{ color: NODE_COLORS.partner, labelKey: 'admin.referralNetwork.legend.partner' },
{ color: NODE_COLORS.topReferrer, labelKey: 'admin.referralNetwork.legend.topReferrer' },
{ color: NODE_COLORS.activeReferrer, labelKey: 'admin.referralNetwork.legend.activeReferrer' },
{ color: NODE_COLORS.campaignUser, labelKey: 'admin.referralNetwork.legend.campaignUser' },
{ color: NODE_COLORS.regular, labelKey: 'admin.referralNetwork.legend.regularUser' },
];
export function NetworkLegend({ className }: NetworkLegendProps) {
@@ -55,22 +52,6 @@ export function NetworkLegend({ className }: NetworkLegendProps) {
))}
</div>
{/* Border = Referral role */}
<h4 className="mb-1.5 mt-2.5 text-[10px] font-medium uppercase tracking-wider text-dark-500">
{t('admin.referralNetwork.legend.borderTitle')}
</h4>
<div className="space-y-1">
{BORDER_ITEMS.map((item) => (
<div key={item.labelKey} className="flex items-center gap-2">
<div
className="h-2.5 w-2.5 shrink-0 rounded-full border-2"
style={{ borderColor: item.color, backgroundColor: 'transparent' }}
/>
<span className="text-xs text-dark-300">{t(item.labelKey)}</span>
</div>
))}
</div>
{/* Edges */}
<div className="mt-2.5 space-y-1">
<div className="flex items-center gap-2">

View File

@@ -37,13 +37,21 @@ export function getSubscriptionStatusColor(status: SubscriptionStatus): string {
}
/**
* Fill color = subscription status (primary concern for campaign evaluation).
* Fill color priority:
* 1. Subscription status (when available)
* 2. Referral role (when no subscription data — keeps visual differentiation)
* 3. Campaign user / regular fallback
*/
export function getNodeFillColor(
subscriptionStatus: SubscriptionStatus | null,
campaignId: number | null,
directReferrals: number = 0,
isPartner: boolean = false,
): string {
if (subscriptionStatus) return SUBSCRIPTION_STATUS_COLOR[subscriptionStatus];
if (isPartner) return NODE_COLORS.partner;
if (directReferrals >= 10) return NODE_COLORS.topReferrer;
if (directReferrals >= 1) return NODE_COLORS.activeReferrer;
if (campaignId !== null) return NODE_COLORS.campaignUser;
return NODE_COLORS.regular;
}