File size: 3,377 Bytes
f878eff
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { useState } from "preact/hooks";
import { useT } from "../../../shared/i18n/context";
import type { AssignmentAccount } from "../../../shared/hooks/use-proxy-assignments";
import type { ProxyEntry } from "../../../shared/types";

interface ProxyGroup {
  id: string;
  label: string;
  count: number;
}

interface ProxyGroupListProps {
  accounts: AssignmentAccount[];
  proxies: ProxyEntry[];
  selectedGroup: string | null;
  onSelectGroup: (groupId: string | null) => void;
}

export function ProxyGroupList({ accounts, proxies, selectedGroup, onSelectGroup }: ProxyGroupListProps) {
  const t = useT();
  const [search, setSearch] = useState("");

  // Build groups with counts
  const groups: ProxyGroup[] = [];

  // "All"
  groups.push({ id: "__all__", label: t("allAccounts"), count: accounts.length });

  // Count by assignment
  const countMap = new Map<string, number>();
  for (const acct of accounts) {
    const key = acct.proxyId || "global";
    countMap.set(key, (countMap.get(key) || 0) + 1);
  }

  // Special groups
  groups.push({ id: "global", label: t("globalDefault"), count: countMap.get("global") || 0 });

  // Proxy entries
  for (const p of proxies) {
    groups.push({ id: p.id, label: p.name, count: countMap.get(p.id) || 0 });
  }

  groups.push({ id: "direct", label: t("directNoProxy"), count: countMap.get("direct") || 0 });
  groups.push({ id: "auto", label: t("autoRoundRobin"), count: countMap.get("auto") || 0 });

  // "Unassigned" — accounts not in any explicit assignment (they default to "global")
  // Since getAssignment defaults to "global", all accounts have a proxyId.
  // "Unassigned" is not really a separate bucket in this model — skip or show 0.

  const lowerSearch = search.toLowerCase();
  const filtered = search
    ? groups.filter((g) => g.label.toLowerCase().includes(lowerSearch))
    : groups;

  const active = selectedGroup ?? "__all__";

  return (
    <div class="flex flex-col gap-1">
      {/* Search */}
      <input
        type="text"
        placeholder={t("searchProxy")}
        value={search}
        onInput={(e) => setSearch((e.target as HTMLInputElement).value)}
        class="px-3 py-2 text-sm border border-gray-200 dark:border-border-dark rounded-lg bg-white dark:bg-bg-dark text-slate-700 dark:text-text-main focus:outline-none focus:ring-1 focus:ring-primary mb-2"
      />

      {filtered.map((g) => {
        const isActive = active === g.id;
        return (
          <button
            key={g.id}
            onClick={() => onSelectGroup(g.id === "__all__" ? null : g.id)}
            class={`flex items-center justify-between px-3 py-2 rounded-lg text-sm transition-colors text-left ${
              isActive
                ? "bg-primary/10 text-primary font-medium border border-primary/20"
                : "hover:bg-slate-50 dark:hover:bg-border-dark text-slate-700 dark:text-text-main"
            }`}
          >
            <span class="truncate">{g.label}</span>
            <span
              class={`ml-2 px-2 py-0.5 rounded-full text-xs font-medium shrink-0 ${
                isActive
                  ? "bg-primary/20 text-primary"
                  : "bg-slate-100 dark:bg-border-dark text-slate-500 dark:text-text-dim"
              }`}
            >
              {g.count}
            </span>
          </button>
        );
      })}
    </div>
  );
}