mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-29 18:13:47 +00:00
- fix top offset breakpoint: sm:top-14 → lg:top-14 to match AppShell header visibility (mobile header is lg:hidden with h-16) - add gravity to FA2 layout to prevent disconnected nodes from flying to extreme positions, making auto-rescale shrink everything to dots - clamp outlier node positions after FA2 finishes (3σ threshold) - add ResizeObserver to sync sigma canvas with container size changes - call sigma.resize() before camera reset after FA2 - increase node sizes: user min 8→12, max 40→50, campaign 18→24 - reduce FA2 duration 5s→3s and slowDown 5→2 for tighter layout
109 lines
4.2 KiB
TypeScript
109 lines
4.2 KiB
TypeScript
import { useTranslation } from 'react-i18next';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import { referralNetworkApi } from '@/api/referralNetwork';
|
|
import { useReferralNetworkStore } from '@/store/referralNetwork';
|
|
import { AdminBackButton } from '@/components/admin/AdminBackButton';
|
|
import { NetworkGraph } from './components/NetworkGraph';
|
|
import { NetworkSearch } from './components/NetworkSearch';
|
|
import { NetworkFilters } from './components/NetworkFilters';
|
|
import { UserDetailPanel } from './components/UserDetailPanel';
|
|
import { CampaignDetailPanel } from './components/CampaignDetailPanel';
|
|
import { NetworkStats } from './components/NetworkStats';
|
|
import { NetworkLegend } from './components/NetworkLegend';
|
|
import { NetworkControls } from './components/NetworkControls';
|
|
|
|
export function ReferralNetwork() {
|
|
const { t } = useTranslation();
|
|
const selectedNode = useReferralNetworkStore((s) => s.selectedNode);
|
|
|
|
const {
|
|
data: networkData,
|
|
isLoading,
|
|
isError,
|
|
} = useQuery({
|
|
queryKey: ['referral-network-graph'],
|
|
queryFn: referralNetworkApi.getNetworkGraph,
|
|
staleTime: 120_000,
|
|
});
|
|
|
|
const isPanelOpen = selectedNode !== null;
|
|
|
|
return (
|
|
<div
|
|
id="referral-network-container"
|
|
className="fixed inset-x-0 bottom-0 top-16 z-40 grid grid-rows-[auto_1fr] bg-[#0a0a0f] lg:top-14"
|
|
>
|
|
<div className="z-20 border-b border-dark-700/50 bg-dark-900/90 backdrop-blur-md">
|
|
<div className="flex items-center gap-2 px-3 py-2 sm:gap-3 sm:px-4 sm:py-3">
|
|
<AdminBackButton />
|
|
<h1 className="shrink-0 text-sm font-bold text-dark-100 sm:text-base">
|
|
{t('admin.referralNetwork.title')}
|
|
</h1>
|
|
<NetworkSearch className="min-w-0 flex-1 sm:max-w-md" />
|
|
{networkData && <NetworkFilters data={networkData} />}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="relative overflow-hidden">
|
|
{isLoading && (
|
|
<div className="absolute inset-0 z-10 flex items-center justify-center">
|
|
<div className="text-center">
|
|
<div className="mx-auto mb-3 h-8 w-8 animate-spin rounded-full border-2 border-dark-600 border-t-accent-400" />
|
|
<p className="text-sm text-dark-400">{t('admin.referralNetwork.loading')}</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{isError && (
|
|
<div className="absolute inset-0 z-10 flex items-center justify-center">
|
|
<p className="text-sm text-error-400">{t('admin.referralNetwork.error')}</p>
|
|
</div>
|
|
)}
|
|
|
|
{networkData && networkData.users.length === 0 && networkData.campaigns.length === 0 && (
|
|
<div className="absolute inset-0 z-10 flex items-center justify-center">
|
|
<p className="text-sm text-dark-500">{t('admin.referralNetwork.empty')}</p>
|
|
</div>
|
|
)}
|
|
|
|
{networkData && (networkData.users.length > 0 || networkData.campaigns.length > 0) && (
|
|
<>
|
|
<NetworkGraph data={networkData} className="absolute inset-0 h-full w-full" />
|
|
|
|
<div className="absolute bottom-3 left-3 z-10 sm:bottom-4 sm:left-4">
|
|
<NetworkStats data={networkData} />
|
|
</div>
|
|
|
|
<div className="absolute bottom-3 right-3 z-10 hidden sm:bottom-4 sm:right-4 sm:block">
|
|
<NetworkLegend />
|
|
</div>
|
|
|
|
<div className="absolute bottom-3 left-1/2 z-10 -translate-x-1/2 sm:bottom-4">
|
|
<NetworkControls />
|
|
</div>
|
|
</>
|
|
)}
|
|
|
|
<div
|
|
className={`absolute right-0 top-0 z-30 flex h-full w-full flex-col border-l border-dark-700/50 bg-dark-900/95 backdrop-blur-md transition-transform duration-300 ease-in-out sm:w-[400px] ${
|
|
isPanelOpen ? 'translate-x-0' : 'translate-x-full'
|
|
}`}
|
|
>
|
|
{selectedNode?.type === 'user' && (
|
|
<UserDetailPanel
|
|
userId={selectedNode.id}
|
|
className="flex flex-1 flex-col overflow-hidden"
|
|
/>
|
|
)}
|
|
{selectedNode?.type === 'campaign' && (
|
|
<CampaignDetailPanel
|
|
campaignId={selectedNode.id}
|
|
className="flex flex-1 flex-col overflow-hidden"
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|