File size: 3,805 Bytes
cd8bd0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"use client";

import { useState } from "react";
import { useTranslations } from "next-intl";
import { ModelSelectorModal } from "./ModelSelectorModal";

export interface MappingRow {
  source: string;
  target: string;
}

interface ModelMappingTableProps {
  agentId: string;
  mappings: MappingRow[];
  onSave: (agentId: string, mappings: MappingRow[]) => Promise<void>;
}

/**
 * Editable table: source model → target OmniRoute model.
 */
export function ModelMappingTable({ agentId, mappings, onSave }: ModelMappingTableProps) {
  const t = useTranslations("agentBridge");
  const [rows, setRows] = useState<MappingRow[]>(mappings);
  const [selectorOpen, setSelectorOpen] = useState<number | null>(null);
  const [saving, setSaving] = useState(false);

  const updateTarget = (index: number, target: string) => {
    setRows((prev) => prev.map((r, i) => (i === index ? { ...r, target } : r)));
    setSelectorOpen(null);
  };

  const handleSave = async () => {
    setSaving(true);
    try {
      await onSave(agentId, rows);
    } finally {
      setSaving(false);
    }
  };

  if (rows.length === 0) {
    return (
      <p className="text-xs text-text-muted italic">
        {t("noMappings") || "No model mappings configured. Run setup wizard to auto-detect models."}
      </p>
    );
  }

  return (
    <div className="flex flex-col gap-3">
      <div className="rounded-lg border border-border/40 overflow-hidden bg-surface">
        <table className="w-full text-sm">
          <thead>
            <tr className="border-b border-border/40 bg-surface/60">
              <th className="px-3 py-2 text-left text-xs font-medium text-text-muted">
                {t("sourceModel") || "Source model (agent native)"}
              </th>
              <th className="px-3 py-2 text-left text-xs font-medium text-text-muted">
                {t("targetModel") || "Target model (OmniRoute)"}
              </th>
            </tr>
          </thead>
          <tbody>
            {rows.map((row, i) => (
              <tr key={i} className="border-b border-border/20 last:border-0">
                <td className="px-3 py-2">
                  <span className="font-mono text-xs text-text-muted">{row.source}</span>
                </td>
                <td className="px-3 py-2">
                  <button
                    type="button"
                    onClick={() => setSelectorOpen(i)}
                    className="inline-flex items-center gap-1.5 rounded-lg border border-border/40 bg-card px-2.5 py-1 text-xs hover:bg-surface transition-colors font-mono"
                  >
                    {row.target || (
                      <span className="text-text-muted italic">
                        {t("selectModel") || "Select…"}
                      </span>
                    )}
                    <span className="material-symbols-outlined text-[12px] text-text-muted">
                      expand_more
                    </span>
                  </button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      <div className="flex justify-end">
        <button
          type="button"
          onClick={handleSave}
          disabled={saving}
          className="rounded-lg bg-primary/10 text-primary px-4 py-1.5 text-sm font-medium hover:bg-primary/20 transition-colors disabled:opacity-50"
        >
          {saving ? t("saving") || "Saving…" : t("saveMappings") || "Save mappings"}
        </button>
      </div>

      {selectorOpen !== null && (
        <ModelSelectorModal
          open
          currentModel={rows[selectorOpen]?.target ?? ""}
          onSelect={(model) => updateTarget(selectorOpen, model)}
          onClose={() => setSelectorOpen(null)}
        />
      )}
    </div>
  );
}