File size: 4,338 Bytes
db764ae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { useState } from "react";
import { api } from "../api";
import type { KeywordAnalysisResponse } from "../types";
import { useApiCall } from "../hooks/useApiCall";
import ScoreBar from "./ScoreBar";
import StatusMessage from "./StatusMessage";

export default function BatchAnalysis() {
  const [keywordsText, setKeywordsText] = useState("");
  const [topK, setTopK] = useState(5);
  const [threshold, setThreshold] = useState(0.4);
  const { data: results, loading, error, run } = useApiCall<Record<string, KeywordAnalysisResponse>>();

  async function handleAnalyze() {
    const keywords = keywordsText.split("\n").map((s) => s.trim()).filter(Boolean);
    if (keywords.length === 0) return;
    await run(() => api.batchAnalyze({ keywords, top_k: topK, cluster_threshold: threshold, compare_across: true }));
  }

  return (
    <div>
      <div className="panel">
        <h2>Batch Keyword Analysis</h2>
        <p className="panel-desc">
          Analyze multiple keywords at once and compare their semantic relationships.
        </p>
        <div className="form-row">
          <div className="form-group">
            <label>Keywords (one per line)</label>
            <textarea
              value={keywordsText}
              onChange={(e) => setKeywordsText(e.target.value)}
              placeholder={`pizza\nschool\nhomework`}
              rows={4}
            />
          </div>
          <div className="flex-col gap-1">
            <div className="form-group form-group-sm">
              <label>Top K</label>
              <input type="number" value={topK} onChange={(e) => setTopK(+e.target.value)} min={1} max={50} />
            </div>
            <div className="form-group form-group-md">
              <label>Cluster Threshold</label>
              <input type="number" value={threshold} onChange={(e) => setThreshold(+e.target.value)} min={0.1} max={1} step={0.05} />
            </div>
          </div>
        </div>
        <button className="btn btn-primary" onClick={handleAnalyze} disabled={loading || !keywordsText.trim()}>
          {loading ? "Analyzing..." : "Analyze All"}
        </button>
      </div>

      {error && <StatusMessage type="err" message={error} />}

      {results && (
        <>
          {Object.values(results).some((a) => Object.keys(a.cross_keyword_similarities).length > 0) && (
            <div className="panel">
              <h3>Cross-Keyword Similarity</h3>
              <table className="data-table">
                <thead>
                  <tr>
                    <th>Keyword</th>
                    {Object.keys(results).map((kw) => (
                      <th key={kw}>{kw}</th>
                    ))}
                  </tr>
                </thead>
                <tbody>
                  {Object.entries(results).map(([kw, analysis]) => (
                    <tr key={kw}>
                      <td style={{ fontWeight: 600 }}>{kw}</td>
                      {Object.keys(results).map((other) => (
                        <td key={other}>
                          {kw === other ? (
                            <span className="text-dim">-</span>
                          ) : (
                            <ScoreBar score={analysis.cross_keyword_similarities[other] ?? 0} />
                          )}
                        </td>
                      ))}
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}

          {Object.entries(results).map(([kw, analysis]) => (
            <div key={kw} className="panel">
              <h3>
                "{kw}" &mdash; {analysis.total_occurrences} occurrence(s),{" "}
                {analysis.meaning_clusters.length} cluster(s)
              </h3>
              {analysis.meaning_clusters.map((cluster) => (
                <div key={cluster.cluster_id} className="result-card mt-1">
                  <div className="result-header">
                    <strong>Cluster {cluster.cluster_id}</strong>
                    <span className="tag">{cluster.size} occurrence(s)</span>
                  </div>
                  <div className="result-text">{cluster.representative_text.slice(0, 200)}...</div>
                </div>
              ))}
            </div>
          ))}
        </>
      )}
    </div>
  );
}