{
  "name": "screen-pipeline",
  "type": "registry:page",
  "dependencies": [
    "@lucide/vue"
  ],
  "registryDependencies": [
    "avatar",
    "badge",
    "breadcrumb",
    "button",
    "card",
    "empty",
    "https://ui.dvizh.website/r/screen-shell.json",
    "scroll-area",
    "separator",
    "sidebar",
    "tooltip"
  ],
  "files": [
    {
      "path": "screens/pipeline/PipelineScreen.vue",
      "type": "registry:file",
      "target": "screens/pipeline/PipelineScreen.vue",
      "content": "<script setup lang=\"ts\">\n/**\n * Воронка канбаном.\n *\n * Сценарий: планёрка отдела. Смотрят не на отдельную сделку, а на форму воронки — где\n * скопилось, где пусто. Поэтому в колонке видно сумму и количество, а карточка сжата до\n * трёх строк: название, компания, деньги.\n *\n * Готовой доски в библиотеке нет, и заводить компонент ради примера нельзя: колонки собраны\n * из `Card` и сетки, карточки — из `Card`. Перетаскивания нет намеренно, оно потребовало бы\n * своего компонента.\n */\nimport { computed } from 'vue'\nimport { PlusIcon, TriangleAlertIcon } from '@lucide/vue'\nimport { Avatar, AvatarFallback } from '@/components/ui/avatar'\nimport { Badge } from '@/components/ui/badge'\nimport {\n  Breadcrumb,\n  BreadcrumbItem,\n  BreadcrumbList,\n  BreadcrumbPage,\n} from '@/components/ui/breadcrumb'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent } from '@/components/ui/card'\nimport { Empty, EmptyDescription, EmptyHeader, EmptyTitle } from '@/components/ui/empty'\nimport { ScrollArea, ScrollBar } from '@/components/ui/scroll-area'\nimport { Separator } from '@/components/ui/separator'\nimport { SidebarInset, SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'\nimport CrmSidebar from '../CrmSidebar.vue'\nimport { dealRows, stageNames } from '../data'\n\nconst money = new Intl.NumberFormat('ru-RU')\n\nconst columns = computed(() =>\n  stageNames.map((stage) => {\n    const cards = dealRows.filter((row) => row.stage === stage)\n    return {\n      stage,\n      cards,\n      amount: cards.reduce((sum, row) => sum + row.amount, 0),\n    }\n  }),\n)\n\nconst riskHint = {\n  overdue: 'Просроченная задача',\n  stuck: 'Пятый день без движения',\n  none: '',\n}\n</script>\n\n<template>\n  <SidebarProvider>\n    <CrmSidebar section=\"Сделки\" />\n    <SidebarInset>\n      <header class=\"flex h-16 shrink-0 items-center gap-2 border-b px-4\">\n        <SidebarTrigger class=\"-ml-1\" />\n        <Separator orientation=\"vertical\" class=\"mr-2 data-[orientation=vertical]:h-4\" />\n        <Breadcrumb>\n          <BreadcrumbList>\n            <BreadcrumbItem><BreadcrumbPage>Воронка</BreadcrumbPage></BreadcrumbItem>\n          </BreadcrumbList>\n        </Breadcrumb>\n        <div class=\"ml-auto flex items-center gap-2\">\n          <Button variant=\"outline\" size=\"sm\" as-child>\n            <RouterLink to=\"/screens/deals\">Списком</RouterLink>\n          </Button>\n          <Button size=\"sm\">\n            <PlusIcon />\n            Сделка\n          </Button>\n        </div>\n      </header>\n\n      <div class=\"flex flex-1 flex-col gap-4 p-4 lg:p-6\">\n        <ScrollArea class=\"w-full\">\n          <div class=\"flex gap-4 pb-4\">\n            <section v-for=\"column in columns\" :key=\"column.stage\" class=\"w-72 shrink-0\">\n              <div class=\"mb-3 flex items-baseline justify-between gap-2\">\n                <div class=\"flex items-center gap-2\">\n                  <h2 class=\"text-sm font-medium\">{{ column.stage }}</h2>\n                  <Badge variant=\"secondary\">{{ column.cards.length }}</Badge>\n                </div>\n                <span class=\"text-muted-foreground text-sm tabular-nums\">\n                  {{ money.format(column.amount) }} ₽\n                </span>\n              </div>\n\n              <div class=\"space-y-2\">\n                <Card\n                  v-for=\"card in column.cards\"\n                  :key=\"card.id\"\n                  size=\"sm\"\n                  class=\"hover:border-foreground/20 transition-colors\"\n                >\n                  <CardContent class=\"space-y-2\">\n                    <div class=\"flex items-start justify-between gap-2\">\n                      <RouterLink\n                        to=\"/screens/deal\"\n                        class=\"text-sm leading-snug font-medium hover:underline\"\n                      >\n                        {{ card.title }}\n                      </RouterLink>\n                      <Tooltip v-if=\"card.risk !== 'none'\">\n                        <TooltipTrigger as-child>\n                          <TriangleAlertIcon class=\"text-destructive mt-0.5 size-4 shrink-0\" />\n                        </TooltipTrigger>\n                        <TooltipContent>{{ riskHint[card.risk] }}</TooltipContent>\n                      </Tooltip>\n                    </div>\n                    <p class=\"text-muted-foreground text-xs\">{{ card.company }}</p>\n                    <div class=\"flex items-center justify-between gap-2\">\n                      <span class=\"text-sm font-medium tabular-nums\">\n                        {{ money.format(card.amount) }} ₽\n                      </span>\n                      <Avatar class=\"size-6\">\n                        <AvatarFallback class=\"text-xs\">{{ card.initials }}</AvatarFallback>\n                      </Avatar>\n                    </div>\n                  </CardContent>\n                </Card>\n\n                <!-- Пустая стадия: на планёрке это самый важный сигнал. -->\n                <Empty v-if=\"!column.cards.length\" class=\"border border-dashed py-8\">\n                  <EmptyHeader>\n                    <EmptyTitle class=\"text-sm\">Пусто</EmptyTitle>\n                    <EmptyDescription class=\"text-xs\">\n                      На этой стадии нет сделок\n                    </EmptyDescription>\n                  </EmptyHeader>\n                </Empty>\n              </div>\n            </section>\n          </div>\n          <ScrollBar orientation=\"horizontal\" />\n        </ScrollArea>\n      </div>\n    </SidebarInset>\n  </SidebarProvider>\n</template>\n"
    }
  ]
}
