/
aaronair
/
Test_task_rt_solution
Обзор
Документация
Войти
/
aaronair
/
Test_task_rt_solution
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/components/InitiativeTable.tsx
126 строк
5 KB
Ilikedrinkpivo
first_commit
28 май 2026, 14:51
28 май 2026, 14:51
b4498fc
Код
Авторство
О чём код?
import { useNavigate } from "react-router-dom"; import type { Initiative } from "@/features/initiatives/types"; import type { SortDirection, SortKey } from "@/features/initiatives/selectors"; import { StatusBadge } from "./StatusBadge"; import { formatMillions } from "@/lib/format"; import { cn } from "@/lib/cn"; interface Props { items: Initiative[]; sortKey: SortKey; sortDirection: SortDirection; onSort: (key: SortKey) => void; } interface Column { key: SortKey | "tech"; label: string; sortable: boolean; className?: string; } const COLUMNS: Column[] = [ { key: "title", label: "Название", sortable: true }, { key: "status", label: "Статус", sortable: true, className: "w-40" }, { key: "segment", label: "Подразделение", sortable: true, className: "w-48" }, { key: "contractor", label: "Исполнитель", sortable: true, className: "w-36" }, { key: "tech", label: "Технологии", sortable: false, className: "w-52" }, { key: "effect", label: "Эффект", sortable: true, className: "w-36" }, { key: "budget", label: "Бюджет", sortable: true, className: "w-32" }, ]; export function InitiativeTable({ items, sortKey, sortDirection, onSort, }: Props) { const navigate = useNavigate(); return ( <div className="card overflow-hidden"> <div className="overflow-x-auto"> <table className="min-w-full text-sm"> <thead> <tr className="bg-surface-muted/60 text-ink-500 text-xs uppercase tracking-wide"> {COLUMNS.map((col) => { const isActive = col.sortable && col.key === sortKey; return ( <th key={col.key} className={cn( "px-4 py-3 text-left font-medium", col.className, col.sortable && "cursor-pointer select-none", )} onClick={() => col.sortable && onSort(col.key as SortKey)} > <span className="inline-flex items-center gap-1"> {col.label} {col.sortable && ( <span className={cn( "transition-opacity", isActive ? "opacity-100" : "opacity-30", )} aria-hidden="true" > {isActive && sortDirection === "desc" ? "▼" : "▲"} </span> )} </span> </th> ); })} </tr> </thead> <tbody> {items.map((it) => ( <tr key={it.id} onClick={() => navigate(`/initiatives/${it.id}`)} className="border-t border-ink-300/30 hover:bg-surface-muted/60 cursor-pointer transition-colors" > <td className="px-4 py-3 align-top"> <div className="font-medium text-ink-900 leading-snug"> {it.title} </div> </td> <td className="px-4 py-3 align-top"> <StatusBadge status={it.status} /> </td> <td className="px-4 py-3 align-top text-ink-700"> {it.segment} </td> <td className="px-4 py-3 align-top text-ink-700"> {it.contractor} </td> <td className="px-4 py-3 align-top"> <div className="flex flex-wrap gap-1"> {it.technologies.slice(0, 2).map((t) => ( <span key={t} className="inline-flex items-center rounded-full bg-primary-50 px-2 py-0.5 text-xs font-medium text-primary-700" > {t} </span> ))} {it.technologies.length > 2 && ( <span className="chip">+{it.technologies.length - 2}</span> )} </div> </td> <td className="px-4 py-3 align-top text-ink-700 tabular-nums text-xs"> {formatMillions(it.businessEffect) ?? "—"} </td> <td className="px-4 py-3 align-top text-ink-700 tabular-nums text-xs"> {formatMillions(it.budget) ?? "—"} </td> </tr> ))} </tbody> </table> </div> </div> ); }