|
| 1 | +import { useEffect, useState } from "react"; |
| 2 | +import { useParams } from "react-router-dom"; |
| 3 | +import { api } from "../api"; |
| 4 | +import { useSSE } from "../SSEContext"; |
| 5 | +import type { CustomizationItem, Customizations } from "../types"; |
| 6 | + |
| 7 | +const SECTIONS: { key: keyof Omit<Customizations, "project">; title: string; blurb: string }[] = [ |
| 8 | + { |
| 9 | + key: "summary_stats", |
| 10 | + title: "Summary stats", |
| 11 | + blurb: "Per-column statistics computed by Buckaroo. def compute(col) -> ibis.Expr", |
| 12 | + }, |
| 13 | + { |
| 14 | + key: "display_klasses", |
| 15 | + title: "Styling classes", |
| 16 | + blurb: "ColAnalysis subclasses that control table rendering and pinned rows.", |
| 17 | + }, |
| 18 | + { |
| 19 | + key: "post_processings", |
| 20 | + title: "Post-processing", |
| 21 | + blurb: "Expression transformers offered in Buckaroo's post-processing dropdown. def process(expr) -> ibis.Expr | DataFrame", |
| 22 | + }, |
| 23 | +]; |
| 24 | + |
| 25 | +function Section({ title, blurb, items }: { title: string; blurb: string; items: CustomizationItem[] }) { |
| 26 | + const active = items.filter((i) => !i.disabled).length; |
| 27 | + return ( |
| 28 | + <div className="cust-section"> |
| 29 | + <div className="cust-section-head"> |
| 30 | + <strong>{title}</strong> |
| 31 | + <span className="meta"> |
| 32 | + {items.length === 0 |
| 33 | + ? "none" |
| 34 | + : `${active} active${items.length - active ? ` · ${items.length - active} disabled` : ""}`} |
| 35 | + </span> |
| 36 | + </div> |
| 37 | + <span className="meta cache-note">{blurb}</span> |
| 38 | + |
| 39 | + {items.length === 0 ? ( |
| 40 | + <div className="nb-empty">none defined</div> |
| 41 | + ) : ( |
| 42 | + items.map((item) => ( |
| 43 | + <details key={item.path} className="cust-item"> |
| 44 | + <summary> |
| 45 | + <span className={`cust-name${item.disabled ? " muted" : ""}`}>{item.name}</span> |
| 46 | + {item.disabled && <span className="vchip">disabled</span>} |
| 47 | + <span className="meta cust-path" title={item.path}> |
| 48 | + {item.path} |
| 49 | + </span> |
| 50 | + </summary> |
| 51 | + <pre className="code">{item.source}</pre> |
| 52 | + </details> |
| 53 | + )) |
| 54 | + )} |
| 55 | + </div> |
| 56 | + ); |
| 57 | +} |
| 58 | + |
| 59 | +export function CustomizationsPage() { |
| 60 | + const { project } = useParams<{ project: string }>(); |
| 61 | + const { version } = useSSE(); |
| 62 | + const [data, setData] = useState<Customizations | null>(null); |
| 63 | + |
| 64 | + useEffect(() => { |
| 65 | + if (!project) return; |
| 66 | + api.customizations(project).then(setData).catch(() => {}); |
| 67 | + }, [project, version]); |
| 68 | + |
| 69 | + return ( |
| 70 | + <main style={{ overflow: "auto" }}> |
| 71 | + <div className="cache-pane"> |
| 72 | + <div className="cache-summary"> |
| 73 | + <strong>Catalog customizations</strong> |
| 74 | + <span className="meta cache-note"> |
| 75 | + Project-wide Buckaroo customizations. They apply to every entry in the catalog and are loaded |
| 76 | + into each session. Read-only here — edit via the MCP tools. |
| 77 | + </span> |
| 78 | + </div> |
| 79 | + |
| 80 | + {SECTIONS.map((s) => ( |
| 81 | + <Section key={s.key} title={s.title} blurb={s.blurb} items={data ? data[s.key] : []} /> |
| 82 | + ))} |
| 83 | + </div> |
| 84 | + </main> |
| 85 | + ); |
| 86 | +} |
0 commit comments