-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathDataInput.tsx
More file actions
119 lines (105 loc) · 3.38 KB
/
DataInput.tsx
File metadata and controls
119 lines (105 loc) · 3.38 KB
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
import { useState, useRef } from "react";
interface DataInputProps {
format: "csv" | "jsonl";
requiredColumns: string[];
placeholder: string;
onData: (data: string) => void;
}
function DataInput({ format, requiredColumns, placeholder, onData }: DataInputProps) {
const [text, setText] = useState("");
const [validationError, setValidationError] = useState<string | null>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const validate = (data: string): string | null => {
const trimmed = data.trim();
if (!trimmed) {
return "No data provided.";
}
if (format === "csv") {
const firstLine = trimmed.split("\n")[0];
const headers = firstLine.split(",").map((h) => h.trim());
const missing = requiredColumns.filter((col) => !headers.includes(col));
if (missing.length > 0) {
return `CSV is missing required column(s): ${missing.join(", ")}. Found headers: ${headers.join(", ")}`;
}
} else {
// jsonl — validate the first line
const firstLine = trimmed.split("\n")[0];
try {
const obj = JSON.parse(firstLine);
const keys = Object.keys(obj);
const missing = requiredColumns.filter((col) => !keys.includes(col));
if (missing.length > 0) {
return `JSONL first line is missing required field(s): ${missing.join(", ")}. Found fields: ${keys.join(", ")}`;
}
} catch {
return "First line is not valid JSON. Expected JSONL format (one JSON object per line).";
}
}
return null;
};
const handleLoad = () => {
const err = validate(text);
if (err) {
setValidationError(err);
return;
}
setValidationError(null);
onData(text.trim());
};
const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (event) => {
const content = event.target?.result;
if (typeof content === "string") {
setText(content);
setValidationError(null);
}
};
reader.readAsText(file);
// Reset file input so the same file can be re-selected
if (fileInputRef.current) {
fileInputRef.current.value = "";
}
};
return (
<div className="data-input">
<div style={{ display: "flex", gap: "0.5rem", marginBottom: "0.5rem" }}>
<button
type="button"
onClick={() => fileInputRef.current?.click()}
className="secondary"
>
Upload {format.toUpperCase()} file
</button>
<input
ref={fileInputRef}
type="file"
accept={format === "csv" ? ".csv" : ".jsonl,.json"}
onChange={handleFileUpload}
style={{ display: "none" }}
/>
<span className="hint">
Required {format === "csv" ? "columns" : "fields"}:{" "}
<code>{requiredColumns.join(", ")}</code>
</span>
</div>
<textarea
value={text}
onChange={(e) => {
setText(e.target.value);
setValidationError(null);
}}
placeholder={placeholder}
rows={8}
spellCheck={false}
/>
{validationError && <div className="error">{validationError}</div>}
<button onClick={handleLoad} disabled={!text.trim()}>
Load Data
</button>
</div>
);
}
export default DataInput;