File size: 5,255 Bytes
6111b2b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
"use client";

import Link from "next/link";
import Image from "next/image";
import type { CliCatalogEntry } from "@/shared/schemas/cliCatalog";
import type { ToolBatchStatus } from "@/shared/types/cliBatchStatus";
import CliStatusBadge from "@/app/(dashboard)/dashboard/cli-code/components/CliStatusBadge";
import { cn } from "@/shared/utils/cn";

export interface CliToolCardProps {
  tool: CliCatalogEntry;
  batchStatus: ToolBatchStatus | null;
  detailHref: string;
  hasActiveProviders: boolean;
}

export default function CliToolCard({

  tool,

  batchStatus,

  detailHref,

  hasActiveProviders,

}: CliToolCardProps) {
  const installed = batchStatus?.detection.installed ?? false;
  const configStatus = batchStatus?.config.status ?? null;
  const version = batchStatus?.detection.version ?? "not found";
  const endpoint = batchStatus?.config.endpoint ?? null;

  const showInstallChips = !installed && tool.configType !== "guide";

  const title = (
    <div className="flex items-center gap-2.5">

      {/* Icon / image */}

      {tool.image ? (

        <Image

          src={tool.image}

          alt={tool.name}

          width={32}

          height={32}

          className="rounded-md object-contain flex-shrink-0"

        />

      ) : (

        <span

          className="material-symbols-outlined text-[20px] flex-shrink-0"

          style={{ color: tool.color }}

          aria-hidden="true"

        >

          {tool.icon ?? "terminal"}

        </span>

      )}

      <div className="min-w-0 flex-1">

        <div className="flex items-center gap-2 flex-wrap">

          <span className="font-semibold text-text-main text-sm leading-tight truncate">

            {tool.name}

          </span>

          <span className="text-[11px] text-text-muted font-mono bg-black/5 dark:bg-white/5 px-1.5 py-0.5 rounded">

            {version}

          </span>

        </div>

        <p className="text-xs text-text-muted line-clamp-1 mt-0.5">{tool.description}</p>

      </div>

      <span className="material-symbols-outlined text-[18px] text-text-muted flex-shrink-0">

        chevron_right

      </span>

    </div>
  );

  return (
    <Link

      href={detailHref}

      className={cn(

        "block min-h-[180px]",

        "bg-surface border border-black/5 dark:border-white/5 rounded-lg shadow-sm",

        "hover:shadow-md hover:border-primary/30 transition-all",

        "p-4 flex flex-col gap-3"

      )}

    >

      {/* Header */}

      {title}



      {/* Status strip */}

      <div className="flex items-center gap-2 flex-wrap">

        {/* Detection */}

        <span

          className={cn(

            "inline-flex items-center gap-1 text-[11px] font-medium px-1.5 py-0.5 rounded",

            installed

              ? "text-green-600 dark:text-green-400"

              : "text-zinc-500 dark:text-zinc-400"

          )}

        >

          <span aria-hidden="true">{installed ? "✓" : "✗"}</span>

          {installed ? "Detectado" : "Não detectado"}

        </span>



        {/* Config status */}

        {configStatus && (

          <CliStatusBadge

            effectiveConfigStatus={configStatus}

            batchStatus={null}

            lastConfiguredAt={batchStatus?.config.lastConfiguredAt ?? null}

          />

        )}



        {/* Endpoint */}

        {endpoint && (

          <span className="text-[10px] text-text-muted font-mono truncate max-w-[140px]">

            {endpoint}

          </span>

        )}

      </div>



      {/* Badges row */}

      <div className="flex items-center gap-1.5 flex-wrap">

        {tool.baseUrlSupport === "partial" && (

          <span className="inline-flex items-center gap-1 px-2 py-0.5 text-[11px] font-medium rounded-full bg-amber-500/10 text-amber-600 dark:text-amber-400">

            <span aria-hidden="true"></span> Base URL parcial

          </span>

        )}

        {tool.acpSpawnable === true && (

          <span className="inline-flex items-center gap-1 px-2 py-0.5 text-[11px] font-medium rounded-full bg-blue-500/10 text-blue-600 dark:text-blue-400">

            também ACP

          </span>

        )}

        {showInstallChips && (

          <>

            <span className="inline-flex items-center gap-1 px-2 py-0.5 text-[11px] font-medium rounded-full bg-black/5 dark:bg-white/5 text-text-muted">

              📋 Manual config

            </span>

            <span className="inline-flex items-center gap-1 px-2 py-0.5 text-[11px] font-medium rounded-full bg-black/5 dark:bg-white/5 text-text-muted">

              ⬇ Install

            </span>

          </>

        )}

      </div>

      {/* Footer */}
      <div className="mt-auto pt-1 flex items-center justify-between">
        <span className="text-xs text-primary font-medium">

          {installed ? "Configurar →" : "Como instalar →"}

        </span>
        {!hasActiveProviders && (
          <span

            className="text-[10px] text-text-muted italic"

            title="Conecte um provider em Providers"

          >

            Conecte um provider em Providers

          </span>
        )}
      </div>
    </Link>
  );
}