feat: color-code referral network nodes by subscription status

Add subscription status colors: green (paid active), blue (trial active),
red (paid expired), orange (trial expired). Extract NODE_COLORS constant
as single source of truth. Add SubscriptionStatus union type. Show status
badge in user detail panel. Add i18n for all 4 locales.
This commit is contained in:
Fringg
2026-03-22 09:07:32 +03:00
parent 8e59af96c5
commit b289ea9c23
9 changed files with 134 additions and 22 deletions

View File

@@ -1,3 +1,5 @@
import type { SubscriptionStatus } from '@/types/referralNetwork';
/**
* Format kopeks to a human-readable ruble string.
*/
@@ -5,6 +7,35 @@ export function formatKopeksToRubles(kopeks: number): string {
return `${(kopeks / 100).toLocaleString('ru-RU')}`;
}
/**
* Single source of truth for user node colors.
*/
export const NODE_COLORS = {
partner: '#f0c261',
topReferrer: '#e85d9a',
activeReferrer: '#7c6aef',
paidActive: '#22c55e',
trialActive: '#38bdf8',
paidExpired: '#ef4444',
trialExpired: '#fb923c',
campaignUser: '#4dd9c0',
regular: '#6b7280',
} as const;
/**
* Map subscription status to its node color.
*/
const SUBSCRIPTION_STATUS_COLOR: Record<SubscriptionStatus, string> = {
paid_active: NODE_COLORS.paidActive,
trial_active: NODE_COLORS.trialActive,
paid_expired: NODE_COLORS.paidExpired,
trial_expired: NODE_COLORS.trialExpired,
};
export function getSubscriptionStatusColor(status: SubscriptionStatus): string {
return SUBSCRIPTION_STATUS_COLOR[status];
}
/**
* Campaign node color palette. Each campaign gets a distinct color
* based on its index position.
@@ -28,17 +59,20 @@ export function getCampaignColor(index: number): string {
/**
* Determine the visual color for a user node.
* Priority: partner > top referrer > active referrer > subscription status > campaign > regular.
*/
export function getUserNodeColor(
directReferrals: number,
isPartner: boolean,
campaignId: number | null,
subscriptionStatus: SubscriptionStatus | null,
): string {
if (isPartner) return '#f0c261';
if (directReferrals >= 10) return '#e85d9a';
if (directReferrals >= 1) return '#7c6aef';
if (campaignId !== null) return '#4dd9c0';
return '#6b7280';
if (isPartner) return NODE_COLORS.partner;
if (directReferrals >= 10) return NODE_COLORS.topReferrer;
if (directReferrals >= 1) return NODE_COLORS.activeReferrer;
if (subscriptionStatus) return SUBSCRIPTION_STATUS_COLOR[subscriptionStatus];
if (campaignId !== null) return NODE_COLORS.campaignUser;
return NODE_COLORS.regular;
}
/**