Upload folder using huggingface_hub
Browse files
client/src/components/Refinity.tsx
CHANGED
|
@@ -26,7 +26,7 @@ const mockTasks: Task[] = [
|
|
| 26 |
|
| 27 |
const Refinity: React.FC = () => {
|
| 28 |
const [stage, setStage] = React.useState<Stage>('task');
|
| 29 |
-
const [tasks] = React.useState<Task[]>(mockTasks);
|
| 30 |
const [selectedTaskId, setSelectedTaskId] = React.useState<string>(mockTasks[0]?.id || '');
|
| 31 |
const [versions, setVersions] = React.useState<Version[]>([]);
|
| 32 |
const [currentVersionId, setCurrentVersionId] = React.useState<string | null>(null);
|
|
@@ -42,6 +42,14 @@ const Refinity: React.FC = () => {
|
|
| 42 |
|
| 43 |
// File upload (.docx placeholder)
|
| 44 |
const [uploading, setUploading] = React.useState(false);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
const task = React.useMemo(() => tasks.find(t => t.id === selectedTaskId) || tasks[0], [tasks, selectedTaskId]);
|
| 47 |
const taskVersions = React.useMemo(() => versions.filter(v => v.taskId === (task?.id || '')), [versions, task?.id]);
|
|
@@ -78,6 +86,34 @@ const Refinity: React.FC = () => {
|
|
| 78 |
}
|
| 79 |
};
|
| 80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
const assignRandom = () => {
|
| 82 |
const pool = taskVersions.filter(v => (v.originalAuthor !== username && v.revisedBy !== username));
|
| 83 |
if (!pool.length) return;
|
|
@@ -135,7 +171,15 @@ const Refinity: React.FC = () => {
|
|
| 135 |
</div>
|
| 136 |
<div className="md:col-span-2">
|
| 137 |
<label className="block text-sm text-gray-700 mb-1">Upload .doc or .docx translation</label>
|
| 138 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
</div>
|
| 140 |
</div>
|
| 141 |
|
|
@@ -150,7 +194,45 @@ const Refinity: React.FC = () => {
|
|
| 150 |
<div className="pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-50" />
|
| 151 |
<span className="relative z-10">Choose Manually</span>
|
| 152 |
</button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
</div>
|
| 155 |
)}
|
| 156 |
|
|
|
|
| 26 |
|
| 27 |
const Refinity: React.FC = () => {
|
| 28 |
const [stage, setStage] = React.useState<Stage>('task');
|
| 29 |
+
const [tasks, setTasks] = React.useState<Task[]>(mockTasks);
|
| 30 |
const [selectedTaskId, setSelectedTaskId] = React.useState<string>(mockTasks[0]?.id || '');
|
| 31 |
const [versions, setVersions] = React.useState<Version[]>([]);
|
| 32 |
const [currentVersionId, setCurrentVersionId] = React.useState<string | null>(null);
|
|
|
|
| 42 |
|
| 43 |
// File upload (.docx placeholder)
|
| 44 |
const [uploading, setUploading] = React.useState(false);
|
| 45 |
+
const fileInputRef = React.useRef<HTMLInputElement | null>(null);
|
| 46 |
+
|
| 47 |
+
// Add Task UI state
|
| 48 |
+
const [showAddTask, setShowAddTask] = React.useState<boolean>(false);
|
| 49 |
+
const [newTaskTitle, setNewTaskTitle] = React.useState<string>('');
|
| 50 |
+
const [newTaskSource, setNewTaskSource] = React.useState<string>('');
|
| 51 |
+
const addTaskFileRef = React.useRef<HTMLInputElement | null>(null);
|
| 52 |
+
const [addingSourceUploading, setAddingSourceUploading] = React.useState<boolean>(false);
|
| 53 |
|
| 54 |
const task = React.useMemo(() => tasks.find(t => t.id === selectedTaskId) || tasks[0], [tasks, selectedTaskId]);
|
| 55 |
const taskVersions = React.useMemo(() => versions.filter(v => v.taskId === (task?.id || '')), [versions, task?.id]);
|
|
|
|
| 86 |
}
|
| 87 |
};
|
| 88 |
|
| 89 |
+
const uploadSourceDoc = async (file: File) => {
|
| 90 |
+
setAddingSourceUploading(true);
|
| 91 |
+
try {
|
| 92 |
+
const base = ((api.defaults as any)?.baseURL as string || '').replace(/\/$/, '');
|
| 93 |
+
const form = new FormData();
|
| 94 |
+
form.append('file', file);
|
| 95 |
+
const resp = await fetch(`${base}/api/refinity/parse`, { method: 'POST', body: form });
|
| 96 |
+
const data = await resp.json().catch(()=>({}));
|
| 97 |
+
if (!resp.ok) throw new Error(data?.error || 'Failed to parse document');
|
| 98 |
+
setNewTaskSource(String(data?.text || ''));
|
| 99 |
+
} finally {
|
| 100 |
+
setAddingSourceUploading(false);
|
| 101 |
+
}
|
| 102 |
+
};
|
| 103 |
+
|
| 104 |
+
const saveNewTask = () => {
|
| 105 |
+
const title = newTaskTitle.trim();
|
| 106 |
+
const src = newTaskSource.trim();
|
| 107 |
+
if (!title || !src) return;
|
| 108 |
+
const id = `t_${Date.now()}`;
|
| 109 |
+
const t: Task = { id, title, sourceText: src };
|
| 110 |
+
setTasks(prev => [...prev, t]);
|
| 111 |
+
setSelectedTaskId(id);
|
| 112 |
+
setShowAddTask(false);
|
| 113 |
+
setNewTaskTitle('');
|
| 114 |
+
setNewTaskSource('');
|
| 115 |
+
};
|
| 116 |
+
|
| 117 |
const assignRandom = () => {
|
| 118 |
const pool = taskVersions.filter(v => (v.originalAuthor !== username && v.revisedBy !== username));
|
| 119 |
if (!pool.length) return;
|
|
|
|
| 171 |
</div>
|
| 172 |
<div className="md:col-span-2">
|
| 173 |
<label className="block text-sm text-gray-700 mb-1">Upload .doc or .docx translation</label>
|
| 174 |
+
<div className="flex items-center gap-3">
|
| 175 |
+
<input ref={fileInputRef} type="file" accept=".doc,.docx" onChange={(e)=>{ const f=e.target.files?.[0]; if(f) uploadDocx(f); if(fileInputRef.current) fileInputRef.current.value=''; }} className="hidden" />
|
| 176 |
+
<button type="button" onClick={()=>fileInputRef.current?.click()} className="relative overflow-hidden inline-flex items-center justify-center gap-2 px-3 py-1.5 text-sm font-medium rounded-2xl text-white ring-1 ring-inset ring-white/50 backdrop-blur-md backdrop-brightness-110 backdrop-saturate-150 bg-indigo-600/70 active:translate-y-0.5 transition-all duration-200">
|
| 177 |
+
<div className="pointer-events-none absolute inset-0 rounded-2xl opacity-60 [background:linear-gradient(to_bottom,rgba(255,255,255,0.35),rgba(255,255,255,0)_28%),linear-gradient(to_right,rgba(255,255,255,0.35),rgba(255,255,255,0)_28%)]" />
|
| 178 |
+
<div className="pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-50" />
|
| 179 |
+
<span className="relative z-10">Choose file</span>
|
| 180 |
+
</button>
|
| 181 |
+
{uploading && <span className="text-sm text-gray-600">Uploading…</span>}
|
| 182 |
+
</div>
|
| 183 |
</div>
|
| 184 |
</div>
|
| 185 |
|
|
|
|
| 194 |
<div className="pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-50" />
|
| 195 |
<span className="relative z-10">Choose Manually</span>
|
| 196 |
</button>
|
| 197 |
+
<button onClick={()=>setShowAddTask(v=>!v)} className="relative overflow-hidden inline-flex items-center justify-center gap-2 px-4 py-2 text-sm font-medium rounded-2xl text-black ring-1 ring-inset ring-white/50 backdrop-blur-md bg-white/30 active:translate-y-0.5 transition-all duration-200">
|
| 198 |
+
<div className="pointer-events-none absolute inset-0 rounded-2xl opacity-60 [background:linear-gradient(to_bottom,rgba(255,255,255,0.35),rgba(255,255,255,0)_28%),linear-gradient(to_right,rgba(255,255,255,0.35),rgba(255,255,255,0)_28%)]" />
|
| 199 |
+
<div className="pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-50" />
|
| 200 |
+
<span className="relative z-10">New Task</span>
|
| 201 |
+
</button>
|
| 202 |
</div>
|
| 203 |
+
|
| 204 |
+
{showAddTask && (
|
| 205 |
+
<div className="mt-6 relative rounded-xl">
|
| 206 |
+
<div className="absolute inset-0 rounded-xl bg-gradient-to-r from-indigo-200/45 via-indigo-100/40 to-indigo-300/45" />
|
| 207 |
+
<div className="relative rounded-xl bg-white/10 backdrop-blur-md ring-1 ring-inset ring-white/30 shadow-[inset_0_0.5px_0_rgba(255,255,255,0.5),inset_0_-1px_1.5px_rgba(0,0,0,0.12)] p-4">
|
| 208 |
+
<div className="pointer-events-none absolute inset-0 rounded-xl opacity-50 [background:linear-gradient(to_bottom,rgba(255,255,255,0.3),rgba(255,255,255,0)_28%),linear-gradient(to_right,rgba(255,255,255,0.28),rgba(255,255,255,0)_28%)]" />
|
| 209 |
+
<div className="pointer-events-none absolute inset-0 rounded-xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-45" />
|
| 210 |
+
<div className="relative grid grid-cols-1 md:grid-cols-3 gap-4 items-start">
|
| 211 |
+
<div>
|
| 212 |
+
<label className="block text-sm text-gray-700 mb-1">Title</label>
|
| 213 |
+
<input value={newTaskTitle} onChange={(e)=>setNewTaskTitle(e.target.value)} className="w-full px-3 py-2 rounded-lg bg-white/50 backdrop-blur border border-ui-border text-gray-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500" />
|
| 214 |
+
</div>
|
| 215 |
+
<div className="md:col-span-2">
|
| 216 |
+
<label className="block text-sm text-gray-700 mb-1">Source Text</label>
|
| 217 |
+
<textarea value={newTaskSource} onChange={(e)=>setNewTaskSource(e.target.value)} rows={4} className="w-full px-4 py-3 border border-ui-border rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 bg-white" placeholder="Paste source text here…" />
|
| 218 |
+
<div className="mt-2 flex items-center gap-3">
|
| 219 |
+
<input ref={addTaskFileRef} type="file" accept=".doc,.docx" onChange={(e)=>{ const f=e.target.files?.[0]; if(f) uploadSourceDoc(f); if(addTaskFileRef.current) addTaskFileRef.current.value=''; }} className="hidden" />
|
| 220 |
+
<button type="button" onClick={()=>addTaskFileRef.current?.click()} className="relative overflow-hidden inline-flex items-center justify-center gap-2 px-3 py-1.5 text-sm font-medium rounded-2xl text-white ring-1 ring-inset ring-white/50 backdrop-blur-md backdrop-brightness-110 backdrop-saturate-150 bg-indigo-600/70 active:translate-y-0.5 transition-all duration-200">
|
| 221 |
+
<div className="pointer-events-none absolute inset-0 rounded-2xl opacity-60 [background:linear-gradient(to_bottom,rgba(255,255,255,0.35),rgba(255,255,255,0)_28%),linear-gradient(to_right,rgba(255,255,255,0.35),rgba(255,255,255,0)_28%)]" />
|
| 222 |
+
<div className="pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-50" />
|
| 223 |
+
<span className="relative z-10">Choose source file</span>
|
| 224 |
+
</button>
|
| 225 |
+
{addingSourceUploading && <span className="text-sm text-gray-600">Parsing…</span>}
|
| 226 |
+
</div>
|
| 227 |
+
</div>
|
| 228 |
+
</div>
|
| 229 |
+
<div className="relative mt-4 flex gap-3">
|
| 230 |
+
<button onClick={saveNewTask} className="relative overflow-hidden inline-flex items-center justify-center gap-2 px-4 py-2 text-sm font-medium rounded-2xl text-white ring-1 ring-inset ring-white/50 backdrop-blur-md backdrop-brightness-110 backdrop-saturate-150 bg-indigo-600/70 active:translate-y-0.5 transition-all duration-200">Save Task</button>
|
| 231 |
+
<button onClick={()=>{ setShowAddTask(false); setNewTaskTitle(''); setNewTaskSource(''); }} className="relative overflow-hidden inline-flex items-center justify-center gap-2 px-4 py-2 text-sm font-medium rounded-2xl text-black ring-1 ring-inset ring-white/50 backdrop-blur-md bg-white/30 active:translate-y-0.5 transition-all duration-200">Cancel</button>
|
| 232 |
+
</div>
|
| 233 |
+
</div>
|
| 234 |
+
</div>
|
| 235 |
+
)}
|
| 236 |
</div>
|
| 237 |
)}
|
| 238 |
|