Bin
2025-12-16 9e0b2ba2c317b1a86212f24cbae3195ad1f3dbfa
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
import { cn } from "@humansignal/ui";
import type { ProviderConfig } from "../types/provider";
 
interface ProviderGridProps {
  providers: Record<string, ProviderConfig>;
  selectedProvider?: string;
  onProviderSelect: (providerName: string) => void;
  error?: string;
}
 
export const ProviderGrid = ({ providers, selectedProvider, onProviderSelect, error }: ProviderGridProps) => {
  return (
    <div className="flex flex-col gap-tight">
      <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-base">
        {Object.entries(providers).map(([_, provider]) => {
          const isSelected = selectedProvider === provider.name;
          const isDisabled = provider.disabled;
          const Icon = provider.icon;
 
          return (
            <button
              key={provider.name}
              type="button"
              onClick={() => onProviderSelect(provider.name)}
              data-testid={`storage-provider-${provider.name}`}
              className={cn(
                "relative p-base border-2 rounded-lg transition-all duration-200 text-center min-h-[120px]",
                "flex flex-col items-center gap-tight relative text-neutral-content",
                "hover:border-primary-border-subtle hover:bg-primary-emphasis-subtle",
                "hover:-translate-y-tightest focus:outline-none focus:ring-2 focus:ring-primary-focus-outline focus:ring-offset-2",
                isSelected
                  ? "border-primary-border-subtle bg-primary-emphasis-subtle shadow-sm"
                  : "border-neutral-border hover:border-primary-border-subtle",
              )}
              aria-pressed={isSelected}
              aria-disabled={isDisabled}
            >
              {Icon && <Icon className="w-8 h-8" />}
              <div className="flex-1 min-w-0 text-center">
                <h3 className="text-body-medium truncate whitespace-pre">{provider.title}</h3>
                {provider.badge && (
                  <div className="mt-1 flex justify-center whitespace-pre absolute -bottom-tight left-1/2 -translate-x-[40px]">
                    {provider.badge}
                  </div>
                )}
              </div>
            </button>
          );
        })}
      </div>
 
      {error && <p className="text-body-small text-negative-content">{error}</p>}
    </div>
  );
};