/* Custom Column Builder (formula builder) =========================== */ const METRIC_TOKENS = [ { val:'spend', label:'Amount Spent', kind:'$' }, { val:'impressions', label:'Impressions', kind:'#' }, { val:'clicks', label:'Clicks', kind:'#' }, { val:'conversions', label:'Conversions', kind:'#' }, { val:'revenue', label:'Revenue', kind:'$' }, { val:'followers_ig', label:'IG Followers', kind:'#' }, { val:'subscribers', label:'Subscribers', kind:'#' }, { val:'video_views', label:'Video Views', kind:'#' }, { val:'addtocart', label:'Add-to-Cart', kind:'#' }, { val:'leads', label:'Leads', kind:'#' }, ]; const OPS = ['+','−','×','÷','(',')']; const FORMAT_OPTS = [ { val:'int', label:'1,234' }, { val:'currency', label:'$1,234' }, { val:'currency2', label:'$1,234.56' }, { val:'pct', label:'12.34%' }, { val:'x', label:'1.23×' }, ]; function TokenChip({ tok, onRemove }){ const isMetric = tok.type === 'metric'; const isOp = tok.type === 'op'; const isNum = tok.type === 'num'; return (
{isMetric && (METRIC_TOKENS.find(m=>m.val===tok.val)?.label || tok.val)} {isOp && {tok.val}} {isNum && {tok.val}} {onRemove && ( )}
); } function CustomColumnBuilder({ open, onClose, onSave, initial }){ const [name, setName] = useState(initial?.name || ''); const [formula, setFormula] = useState(initial?.formula || []); const [format, setFormat] = useState(initial?.format || 'currency2'); const [numInput, setNumInput] = useState(''); useEffect(()=>{ if (open) { setName(initial?.name || ''); setFormula(initial?.formula || []); setFormat(initial?.format || 'currency2'); setNumInput(''); } }, [open, initial]); const add = (tok)=> setFormula(f => [...f, tok]); const remove = (i)=> setFormula(f => f.filter((_,idx)=>idx!==i)); const clear = ()=> setFormula([]); /* Preview a stylized formula string */ const preview = formula.length === 0 ? 'Build your formula below…' : formula.map(t => t.type==='metric' ? `[${(METRIC_TOKENS.find(m=>m.val===t.val)?.label||t.val)}]` : t.val).join(' '); /* Sample value preview (fake) */ const sampleValue = useMemo(()=>{ const r = formula.length ? (Math.random()*42+1) : 0; return formula.length ? fmt(+r.toFixed(2), format) : '—'; }, [formula, format]); return (
Create custom column
Define a metric using existing fields and operators.
{/* Name + format */}
{/* Formula canvas */}
Formula
{formula.length === 0 && ( Tap metrics and operators below to build → )} {formula.map((t,i)=>(remove(i)}/>))}
{/* Preview line */}
{preview} {sampleValue}
{/* Token palette */}
Metrics
{METRIC_TOKENS.map(m => ( ))}
Operators
{OPS.map(op => ( ))}
Constant
setNumInput(e.target.value)} placeholder="123" className="w-full h-8 rounded-lg border border-ink-200 dark:border-white/10 bg-white dark:bg-ink-900/40 px-2 text-[12.5px] font-mono"/>
Saved columns are pinned to your view.
Cancel { if(!name.trim() || formula.length===0) return; onSave({ id:'cc_'+Date.now(), name, formula, format }); onClose(); }}> Save column
); } Object.assign(window, { CustomColumnBuilder });