TeamVersions.tsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. import { useTranslation } from "react-i18next";
  2. import { FileCode2 } from "lucide-react";
  3. import { EmptyState } from "@/components/shared/EmptyState";
  4. import { LoadingSpinner } from "@/components/shared/LoadingSpinner";
  5. import { StatusBadge } from "@/components/shared/StatusBadge";
  6. import { Badge } from "@/components/ui/badge";
  7. import type { TeamVersion } from "@/types";
  8. import { formatDateTime, truncateMiddle } from "@/lib/utils";
  9. export function TeamVersions({ versions, loading }: { versions: TeamVersion[]; loading: boolean }) {
  10. const { t } = useTranslation();
  11. if (loading) return <LoadingSpinner label={t("common.loading")} />;
  12. const sorted = [...versions].sort((a, b) => b.version_no - a.version_no);
  13. if (!sorted.length) {
  14. return <EmptyState icon={FileCode2} title={t("teams.noVersion")} description={t("teams.createVersionDefine")} />;
  15. }
  16. return (
  17. <div className="space-y-3">
  18. {sorted.map((version) => (
  19. <div key={version.id} className="rounded-md border border-border bg-muted/30 p-4">
  20. <div className="flex flex-wrap items-start justify-between gap-3">
  21. <div>
  22. <div className="flex flex-wrap items-center gap-2">
  23. <p className="font-medium">v{version.version_no}</p>
  24. <StatusBadge status={version.status} />
  25. <Badge className="border-border bg-muted/60 text-muted-foreground">{readableLabel(version.coordination_mode)}</Badge>
  26. </div>
  27. <p className="mt-2 text-sm leading-6 text-muted-foreground">{version.objective ?? t("teams.noObjectiveProvided")}</p>
  28. </div>
  29. <p className="text-xs text-muted-foreground">{formatDateTime(version.created_time)}</p>
  30. </div>
  31. <div className="mt-4 grid gap-3 text-sm md:grid-cols-3">
  32. <div className="min-w-0">
  33. <p className="text-muted-foreground">{t("teams.members")}</p>
  34. <p className="mt-1">{version.member_refs_json.length}</p>
  35. </div>
  36. <div className="min-w-0">
  37. <p className="text-muted-foreground">{t("agents.published")}</p>
  38. <p className="mt-1">{version.published_time ? formatDateTime(version.published_time) : t("teams.notPublished")}</p>
  39. </div>
  40. <div className="min-w-0">
  41. <p className="text-muted-foreground">{t("teams.versionId")}</p>
  42. <p className="mt-1 truncate font-mono text-xs">{truncateMiddle(version.id, 28)}</p>
  43. </div>
  44. </div>
  45. </div>
  46. ))}
  47. </div>
  48. );
  49. }
  50. function readableLabel(value: string) {
  51. return value.split(/[_-]+/).filter(Boolean).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
  52. }