{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "demo-sandbox-panel",
  "title": "Demo — Sandbox Panel",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-vue/blocks/demo-sandbox-panel/DemoSandboxPanel.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed, reactive } from 'vue'\nimport { RotateCcw } from 'lucide-vue-next'\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 form = reactive({ ...DEFAULTS })\n\n// Both outputs derive from the same form, so the query and the result can never\n// describe different things — the failure mode of most fake sandboxes.\nconst query = computed(\n  () => `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\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\nconst results = computed(() =>\n  (BASE[form.groupBy] ?? BASE.region).map((row) => ({\n    key: row.key,\n    value: Math.round(row.value * (MULTIPLIER[form.window] ?? 1)),\n  })),\n)\n\nfunction reset() {\n  Object.assign(form, DEFAULTS)\n}\n</script>\n\n<template>\n  <section data-slot=\"demo-sandbox-panel\" class=\"bg-background\">\n    <div class=\"mx-auto max-w-5xl px-6 py-20 lg:py-28\">\n      <div class=\"flex flex-wrap items-end justify-between gap-6\">\n        <div class=\"max-w-xl\">\n          <Badge variant=\"secondary\">Sandbox</Badge>\n          <h2 class=\"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\" @click=\"reset\">\n          <RotateCcw class=\"mr-1.5 size-3.5\" aria-hidden=\"true\" />\n          Reset\n        </Button>\n      </div>\n\n      <Card class=\"mt-8\">\n        <CardContent class=\"grid gap-6 p-6 lg:grid-cols-[18rem_1fr] lg:gap-8\">\n          <div class=\"space-y-4\">\n            <div>\n              <label for=\"sandbox-metric\" class=\"text-sm font-medium\">Metric</label>\n              <Input id=\"sandbox-metric\" v-model=\"form.metric\" class=\"mt-1.5 font-mono text-sm\" />\n            </div>\n\n            <div>\n              <label for=\"sandbox-window\" class=\"text-sm font-medium\">Window</label>\n              <Select v-model=\"form.window\">\n                <SelectTrigger id=\"sandbox-window\" class=\"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 for=\"sandbox-group\" class=\"text-sm font-medium\">Group by</label>\n              <Select v-model=\"form.groupBy\">\n                <SelectTrigger id=\"sandbox-group\" class=\"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 class=\"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 default-value=\"result\">\n            <TabsList variant=\"segmented\">\n              <TabsTrigger value=\"result\" variant=\"segmented\">Result</TabsTrigger>\n              <TabsTrigger value=\"query\" variant=\"segmented\">Generated query</TabsTrigger>\n            </TabsList>\n\n            <TabsContent value=\"result\" class=\"mt-4\">\n              <div class=\"border-border min-h-[12rem] overflow-hidden rounded-lg border\">\n                <div class=\"border-border text-muted-foreground border-b px-4 py-2 font-mono text-xs\">\n                  {{ form.metric }} · {{ form.window }}\n                </div>\n                <ul class=\"divide-border divide-y\">\n                  <li v-for=\"row in results\" :key=\"row.key\" class=\"flex items-center justify-between gap-4 px-4 py-2.5\">\n                    <span class=\"text-sm\">{{ row.key }}</span>\n                    <span class=\"font-mono text-sm tabular-nums\">£{{ row.value.toLocaleString('en-GB') }}</span>\n                  </li>\n                </ul>\n              </div>\n            </TabsContent>\n\n            <TabsContent value=\"query\" class=\"mt-4\">\n              <pre\n                class=\"border-border min-h-[12rem] overflow-x-auto rounded-lg border p-4 font-mono text-xs leading-relaxed\"\n              ><code>{{ query }}</code></pre>\n            </TabsContent>\n          </Tabs>\n        </CardContent>\n      </Card>\n    </div>\n  </section>\n</template>\n",
      "type": "registry:block",
      "target": "~/app/components/blocks/DemoSandboxPanel.vue"
    }
  ],
  "dependencies": [
    "lucide-vue-next"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/badge.json",
    "https://uipkge.dev/r/vue/button.json",
    "https://uipkge.dev/r/vue/card.json",
    "https://uipkge.dev/r/vue/input.json",
    "https://uipkge.dev/r/vue/select.json",
    "https://uipkge.dev/r/vue/separator.json",
    "https://uipkge.dev/r/vue/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"
  ]
}