108 lines
3.3 KiB
TypeScript
108 lines
3.3 KiB
TypeScript
'use client'
|
|
|
|
import { useCallback, useState } from 'react'
|
|
import type { SplitsConfig } from '@/lib/types'
|
|
|
|
interface SplitsViewProps {
|
|
config: SplitsConfig
|
|
}
|
|
|
|
const CHAIN_NAMES: Record<number, string> = {
|
|
100: 'Gnosis',
|
|
10: 'Optimism',
|
|
1: 'Ethereum',
|
|
}
|
|
|
|
function truncateAddress(addr: string): string {
|
|
return `${addr.slice(0, 6)}...${addr.slice(-4)}`
|
|
}
|
|
|
|
const BAR_COLORS = [
|
|
'#3b82f6', '#8b5cf6', '#ec4899', '#06b6d4', '#10b981',
|
|
'#f59e0b', '#ef4444', '#6366f1', '#14b8a6', '#f97316',
|
|
]
|
|
|
|
export default function SplitsView({ config }: SplitsViewProps) {
|
|
const [copied, setCopied] = useState(false)
|
|
|
|
const handleExport = useCallback(() => {
|
|
const exportData = {
|
|
type: '0xSplits',
|
|
chainId: config.chainId,
|
|
distributorFee: config.distributorFee,
|
|
recipients: config.recipients.map((r) => ({
|
|
address: r.address,
|
|
percentAllocation: r.percentage * 10000, // 0xSplits uses basis points (ppm)
|
|
})),
|
|
}
|
|
navigator.clipboard.writeText(JSON.stringify(exportData, null, 2)).then(() => {
|
|
setCopied(true)
|
|
setTimeout(() => setCopied(false), 2000)
|
|
})
|
|
}, [config])
|
|
|
|
const totalPct = config.recipients.reduce((s, r) => s + r.percentage, 0)
|
|
const chainName = CHAIN_NAMES[config.chainId] || `Chain ${config.chainId}`
|
|
|
|
return (
|
|
<div className="space-y-3">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-xs font-semibold text-slate-700 uppercase tracking-wide">
|
|
0xSplits Config
|
|
</span>
|
|
<span className="text-[10px] px-1.5 py-0.5 bg-slate-100 text-slate-500 rounded">
|
|
{chainName}
|
|
</span>
|
|
</div>
|
|
<span className="text-[10px] text-slate-400">
|
|
Fee: {config.distributorFee}%
|
|
</span>
|
|
</div>
|
|
|
|
{/* Combined percentage bar */}
|
|
<div className="h-3 bg-slate-100 rounded-full overflow-hidden flex">
|
|
{config.recipients.map((r, i) => (
|
|
<div
|
|
key={r.address}
|
|
className="h-full transition-all"
|
|
style={{
|
|
width: `${(r.percentage / totalPct) * 100}%`,
|
|
backgroundColor: BAR_COLORS[i % BAR_COLORS.length],
|
|
}}
|
|
/>
|
|
))}
|
|
</div>
|
|
|
|
{/* Recipient list */}
|
|
<div className="space-y-1.5">
|
|
{config.recipients.map((r, i) => (
|
|
<div key={r.address} className="flex items-center gap-2 text-xs">
|
|
<div
|
|
className="w-2.5 h-2.5 rounded-sm flex-shrink-0"
|
|
style={{ backgroundColor: BAR_COLORS[i % BAR_COLORS.length] }}
|
|
/>
|
|
<span className="font-mono text-slate-600 flex-1 truncate" title={r.address}>
|
|
{r.label || truncateAddress(r.address)}
|
|
</span>
|
|
<span className="font-mono font-medium text-slate-800">{r.percentage}%</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{totalPct !== 100 && (
|
|
<p className="text-[10px] text-amber-600">
|
|
Total is {totalPct}% (should be 100%)
|
|
</p>
|
|
)}
|
|
|
|
<button
|
|
onClick={handleExport}
|
|
className="w-full px-3 py-1.5 rounded-lg text-xs font-medium border border-slate-300 text-slate-600 hover:bg-slate-50 transition-all"
|
|
>
|
|
{copied ? 'Copied to clipboard!' : 'Export Config JSON'}
|
|
</button>
|
|
</div>
|
|
)
|
|
}
|