mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-29 18:13:47 +00:00
Campaign users no longer get a separate amber fill color. Their campaign association is already visible through the edge connecting them to the campaign node. Users from campaigns now show their subscription status color (or regular gray if no subscription), reducing visual noise.
97 lines
2.7 KiB
TypeScript
97 lines
2.7 KiB
TypeScript
import type { SubscriptionStatus } from '@/types/referralNetwork';
|
|
|
|
/**
|
|
* Format kopeks to a human-readable ruble string.
|
|
*/
|
|
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: '#fbbf24',
|
|
regular: '#94a3b8',
|
|
} 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];
|
|
}
|
|
|
|
/**
|
|
* Fill color priority:
|
|
* 1. Subscription status (when available)
|
|
* 2. Referral role (when no subscription data — keeps visual differentiation)
|
|
* 3. Regular fallback (campaign membership visible via edges, not fill)
|
|
*/
|
|
export function getNodeFillColor(
|
|
subscriptionStatus: SubscriptionStatus | 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;
|
|
return NODE_COLORS.regular;
|
|
}
|
|
|
|
/**
|
|
* Border color = referral role. Returns null if no special role.
|
|
*/
|
|
export function getNodeBorderColor(directReferrals: number, isPartner: boolean): string | null {
|
|
if (isPartner) return NODE_COLORS.partner;
|
|
if (directReferrals >= 10) return NODE_COLORS.topReferrer;
|
|
if (directReferrals >= 1) return NODE_COLORS.activeReferrer;
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Campaign node color palette. Each campaign gets a distinct color
|
|
* based on its index position.
|
|
*/
|
|
const CAMPAIGN_COLORS = [
|
|
'#4dd9c0',
|
|
'#f0c261',
|
|
'#e85d9a',
|
|
'#6b9fff',
|
|
'#b97aff',
|
|
'#ff8a65',
|
|
'#66d9a0',
|
|
'#ff6b9d',
|
|
'#7ec8e3',
|
|
'#c4b5fd',
|
|
];
|
|
|
|
export function getCampaignColor(index: number): string {
|
|
return CAMPAIGN_COLORS[index % CAMPAIGN_COLORS.length];
|
|
}
|
|
|
|
/**
|
|
* Determine the visual size for a user node.
|
|
* Size is proportional to direct_referrals, clamped between min and max.
|
|
*/
|
|
export function getUserNodeSize(directReferrals: number): number {
|
|
if (directReferrals === 0) return 5;
|
|
if (directReferrals <= 2) return 10;
|
|
return Math.min(40, 10 + Math.sqrt(directReferrals) * 4);
|
|
}
|