{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "demo-sandbox-panel",
  "title": "Demo — Sandbox Panel",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-react/blocks/demo-sandbox-panel/DemoSandboxPanel.tsx",
      "content": "'use client'\n\nimport { useState } from 'react'\nimport { RotateCcw } from 'lucide-react'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent } from '@/components/ui/card'\nimport { Input } from '@/components/ui/input'\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'\nimport { Separator } from '@/components/ui/separator'\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'\n\nconst DEFAULTS = { metric: 'revenue_net', window: 'trailing_28d', groupBy: 'region' }\n\nconst BASE: Record<string, { key: string; value: number }[]> = {\n  region: [\n    { key: 'EMEA', value: 1_842_100 },\n    { key: 'AMER', value: 1_536_400 },\n    { key: 'APAC', value: 762_400 },\n  ],\n  channel: [\n    { key: 'Direct', value: 2_104_900 },\n    { key: 'Marketplace', value: 1_218_600 },\n    { key: 'Wholesale', value: 817_400 },\n  ],\n}\n\nconst MULTIPLIER: Record<string, number> = { trailing_7d: 0.25, trailing_28d: 1, trailing_90d: 3.1 }\n\nexport function DemoSandboxPanel() {\n  const [form, setForm] = useState({ ...DEFAULTS })\n  const set = (key: keyof typeof DEFAULTS, value: string) => setForm((f) => ({ ...f, [key]: value }))\n\n  // Both outputs derive from the same form, so the query and the result can\n  // never describe different things — the failure mode of most fake sandboxes.\n  const query = `SELECT ${form.groupBy},\n       SUM(amount) AS ${form.metric}\nFROM   metrics.${form.metric}\nWHERE  window = '${form.window}'\nGROUP  BY ${form.groupBy}\nORDER  BY ${form.metric} DESC`\n\n  const results = (BASE[form.groupBy] ?? BASE.region).map((row) => ({\n    key: row.key,\n    value: Math.round(row.value * (MULTIPLIER[form.window] ?? 1)),\n  }))\n\n  return (\n    <section data-slot=\"demo-sandbox-panel\" className=\"bg-background\">\n      <div className=\"mx-auto max-w-5xl px-6 py-20 lg:py-28\">\n        <div className=\"flex flex-wrap items-end justify-between gap-6\">\n          <div className=\"max-w-xl\">\n            <Badge variant=\"secondary\">Sandbox</Badge>\n            <h2 className=\"mt-4 text-3xl font-semibold tracking-tight sm:text-4xl\">\n              Change the definition, watch both sides\n            </h2>\n          </div>\n          <Button variant=\"ghost\" size=\"sm\" onClick={() => setForm({ ...DEFAULTS })}>\n            <RotateCcw className=\"mr-1.5 size-3.5\" aria-hidden=\"true\" />\n            Reset\n          </Button>\n        </div>\n\n        <Card className=\"mt-8\">\n          <CardContent className=\"grid gap-6 p-6 lg:grid-cols-[18rem_1fr] lg:gap-8\">\n            <div className=\"space-y-4\">\n              <div>\n                <label htmlFor=\"sandbox-metric\" className=\"text-sm font-medium\">\n                  Metric\n                </label>\n                <Input\n                  id=\"sandbox-metric\"\n                  value={form.metric}\n                  onChange={(event) => set('metric', event.target.value)}\n                  className=\"mt-1.5 font-mono text-sm\"\n                />\n              </div>\n\n              <div>\n                <label htmlFor=\"sandbox-window\" className=\"text-sm font-medium\">\n                  Window\n                </label>\n                <Select value={form.window} onValueChange={(value) => set('window', value)}>\n                  <SelectTrigger id=\"sandbox-window\" className=\"mt-1.5\">\n                    <SelectValue />\n                  </SelectTrigger>\n                  <SelectContent>\n                    <SelectItem value=\"trailing_7d\">trailing_7d</SelectItem>\n                    <SelectItem value=\"trailing_28d\">trailing_28d</SelectItem>\n                    <SelectItem value=\"trailing_90d\">trailing_90d</SelectItem>\n                  </SelectContent>\n                </Select>\n              </div>\n\n              <div>\n                <label htmlFor=\"sandbox-group\" className=\"text-sm font-medium\">\n                  Group by\n                </label>\n                <Select value={form.groupBy} onValueChange={(value) => set('groupBy', value)}>\n                  <SelectTrigger id=\"sandbox-group\" className=\"mt-1.5\">\n                    <SelectValue />\n                  </SelectTrigger>\n                  <SelectContent>\n                    <SelectItem value=\"region\">region</SelectItem>\n                    <SelectItem value=\"channel\">channel</SelectItem>\n                  </SelectContent>\n                </Select>\n              </div>\n\n              <Separator />\n              <p className=\"text-muted-foreground text-xs leading-relaxed\">\n                Both panels recompute from these three fields. Nothing is cached between them.\n              </p>\n            </div>\n\n            <Tabs defaultValue=\"result\">\n              <TabsList variant=\"segmented\">\n                <TabsTrigger value=\"result\" variant=\"segmented\">\n                  Result\n                </TabsTrigger>\n                <TabsTrigger value=\"query\" variant=\"segmented\">\n                  Generated query\n                </TabsTrigger>\n              </TabsList>\n\n              <TabsContent value=\"result\" className=\"mt-4\">\n                <div className=\"border-border min-h-[12rem] overflow-hidden rounded-lg border\">\n                  <div className=\"border-border text-muted-foreground border-b px-4 py-2 font-mono text-xs\">\n                    {form.metric} · {form.window}\n                  </div>\n                  <ul className=\"divide-border divide-y\">\n                    {results.map((row) => (\n                      <li key={row.key} className=\"flex items-center justify-between gap-4 px-4 py-2.5\">\n                        <span className=\"text-sm\">{row.key}</span>\n                        <span className=\"font-mono text-sm tabular-nums\">£{row.value.toLocaleString('en-GB')}</span>\n                      </li>\n                    ))}\n                  </ul>\n                </div>\n              </TabsContent>\n\n              <TabsContent value=\"query\" className=\"mt-4\">\n                <pre className=\"border-border min-h-[12rem] overflow-x-auto rounded-lg border p-4 font-mono text-xs leading-relaxed\">\n                  <code>{query}</code>\n                </pre>\n              </TabsContent>\n            </Tabs>\n          </CardContent>\n        </Card>\n      </div>\n    </section>\n  )\n}\n",
      "type": "registry:block",
      "target": "~/components/blocks/DemoSandboxPanel.tsx"
    }
  ],
  "dependencies": [
    "lucide-react"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/react/badge.json",
    "https://uipkge.dev/r/react/button.json",
    "https://uipkge.dev/r/react/card.json",
    "https://uipkge.dev/r/react/input.json",
    "https://uipkge.dev/r/react/select.json",
    "https://uipkge.dev/r/react/separator.json",
    "https://uipkge.dev/r/react/tabs.json"
  ],
  "description": "Interactive sandbox panel where editing a definition field updates a rendered result and a generated query side by side, with a reset control.",
  "categories": [
    "marketing"
  ]
}