Upload folder using huggingface_hub
Browse files
client/src/components/Refinity.tsx
CHANGED
|
@@ -53,6 +53,7 @@ const Refinity: React.FC = () => {
|
|
| 53 |
const [addingSourceUploading, setAddingSourceUploading] = React.useState<boolean>(false);
|
| 54 |
const [taskMenuOpen, setTaskMenuOpen] = React.useState<boolean>(false);
|
| 55 |
const [pastedTranslation, setPastedTranslation] = React.useState<string>('');
|
|
|
|
| 56 |
|
| 57 |
const task = React.useMemo(() => tasks.find(t => t.id === selectedTaskId) || tasks[0], [tasks, selectedTaskId]);
|
| 58 |
const taskVersions = React.useMemo(() => versions.filter(v => v.taskId === (task?.id || '')), [versions, task?.id]);
|
|
@@ -150,7 +151,7 @@ const Refinity: React.FC = () => {
|
|
| 150 |
|
| 151 |
const assignRandom = () => {
|
| 152 |
const pool = taskVersions.filter(v => (v.originalAuthor !== username && v.revisedBy !== username));
|
| 153 |
-
if (!pool.length) return;
|
| 154 |
const pick = pool[Math.floor(Math.random() * pool.length)];
|
| 155 |
setCurrentVersionId(pick.id);
|
| 156 |
setStage('editor');
|
|
@@ -177,21 +178,37 @@ const Refinity: React.FC = () => {
|
|
| 177 |
setStage('editor');
|
| 178 |
};
|
| 179 |
|
| 180 |
-
const handleSaveRevision = (newContent: string) => {
|
| 181 |
const parent = taskVersions.find(v => v.id === currentVersionId);
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
};
|
| 196 |
|
| 197 |
return (
|
|
@@ -258,8 +275,8 @@ const Refinity: React.FC = () => {
|
|
| 258 |
</div>
|
| 259 |
</div>
|
| 260 |
|
| 261 |
-
<div className="mt-8 flex gap-3">
|
| 262 |
-
<button onClick={assignRandom} 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">
|
| 263 |
<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%)]" />
|
| 264 |
<div className="pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-50" />
|
| 265 |
<span className="relative z-10">Random Mode</span>
|
|
@@ -276,6 +293,7 @@ const Refinity: React.FC = () => {
|
|
| 276 |
<div className="pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-50" />
|
| 277 |
<span className="relative z-10">New Task</span>
|
| 278 |
</button>
|
|
|
|
| 279 |
</div>
|
| 280 |
|
| 281 |
{showAddTask && (
|
|
@@ -351,7 +369,7 @@ const Refinity: React.FC = () => {
|
|
| 351 |
<button onClick={()=>{ setPreviewVersionId(v.id); setStage('preview'); }} className="relative overflow-hidden inline-flex items-center justify-center gap-2 px-3 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">
|
| 352 |
<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%)]" />
|
| 353 |
<div className="pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-50" />
|
| 354 |
-
<span className="relative z-10">
|
| 355 |
</button>
|
| 356 |
<button onClick={()=>selectManual(v.id)} className="relative overflow-hidden inline-flex items-center justify-center gap-2 px-3 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">
|
| 357 |
<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%)]" />
|
|
|
|
| 53 |
const [addingSourceUploading, setAddingSourceUploading] = React.useState<boolean>(false);
|
| 54 |
const [taskMenuOpen, setTaskMenuOpen] = React.useState<boolean>(false);
|
| 55 |
const [pastedTranslation, setPastedTranslation] = React.useState<string>('');
|
| 56 |
+
const [taskStageNote, setTaskStageNote] = React.useState<string>('');
|
| 57 |
|
| 58 |
const task = React.useMemo(() => tasks.find(t => t.id === selectedTaskId) || tasks[0], [tasks, selectedTaskId]);
|
| 59 |
const taskVersions = React.useMemo(() => versions.filter(v => v.taskId === (task?.id || '')), [versions, task?.id]);
|
|
|
|
| 151 |
|
| 152 |
const assignRandom = () => {
|
| 153 |
const pool = taskVersions.filter(v => (v.originalAuthor !== username && v.revisedBy !== username));
|
| 154 |
+
if (!pool.length) { setTaskStageNote('No eligible translations to revise yet. Try Manual Selection or add a translation.'); return; }
|
| 155 |
const pick = pool[Math.floor(Math.random() * pool.length)];
|
| 156 |
setCurrentVersionId(pick.id);
|
| 157 |
setStage('editor');
|
|
|
|
| 178 |
setStage('editor');
|
| 179 |
};
|
| 180 |
|
| 181 |
+
const handleSaveRevision = async (newContent: string) => {
|
| 182 |
const parent = taskVersions.find(v => v.id === currentVersionId);
|
| 183 |
+
try {
|
| 184 |
+
const base = ((api.defaults as any)?.baseURL as string || '').replace(/\/$/, '');
|
| 185 |
+
const resp = await fetch(`${base}/api/refinity/tasks/${encodeURIComponent(task?.id || '')}/versions`, {
|
| 186 |
+
method: 'POST',
|
| 187 |
+
headers: { 'Content-Type': 'application/json' },
|
| 188 |
+
body: JSON.stringify({
|
| 189 |
+
originalAuthor: parent?.originalAuthor || username,
|
| 190 |
+
revisedBy: username,
|
| 191 |
+
content: newContent,
|
| 192 |
+
parentVersionId: parent?.id,
|
| 193 |
+
})
|
| 194 |
+
});
|
| 195 |
+
const saved = await resp.json().catch(()=>({}));
|
| 196 |
+
if (!resp.ok) throw new Error(saved?.error || 'Save failed');
|
| 197 |
+
const v: Version = {
|
| 198 |
+
id: saved._id,
|
| 199 |
+
taskId: saved.taskId,
|
| 200 |
+
originalAuthor: saved.originalAuthor,
|
| 201 |
+
revisedBy: saved.revisedBy,
|
| 202 |
+
versionNumber: saved.versionNumber,
|
| 203 |
+
content: saved.content,
|
| 204 |
+
parentVersionId: saved.parentVersionId,
|
| 205 |
+
};
|
| 206 |
+
setVersions(prev => [...prev, v]);
|
| 207 |
+
setCurrentVersionId(v.id);
|
| 208 |
+
setStage('flow');
|
| 209 |
+
} catch {
|
| 210 |
+
// no-op; keep user in editor if needed
|
| 211 |
+
}
|
| 212 |
};
|
| 213 |
|
| 214 |
return (
|
|
|
|
| 275 |
</div>
|
| 276 |
</div>
|
| 277 |
|
| 278 |
+
<div className="mt-8 flex gap-3 items-center flex-wrap">
|
| 279 |
+
<button onClick={assignRandom} disabled={!taskVersions.some(v=>v.originalAuthor!==username && v.revisedBy!==username)} 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 disabled:bg-gray-400 active:translate-y-0.5 transition-all duration-200">
|
| 280 |
<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%)]" />
|
| 281 |
<div className="pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-50" />
|
| 282 |
<span className="relative z-10">Random Mode</span>
|
|
|
|
| 293 |
<div className="pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-50" />
|
| 294 |
<span className="relative z-10">New Task</span>
|
| 295 |
</button>
|
| 296 |
+
{taskStageNote && <span className="text-sm text-gray-600 ml-2">{taskStageNote}</span>}
|
| 297 |
</div>
|
| 298 |
|
| 299 |
{showAddTask && (
|
|
|
|
| 369 |
<button onClick={()=>{ setPreviewVersionId(v.id); setStage('preview'); }} className="relative overflow-hidden inline-flex items-center justify-center gap-2 px-3 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">
|
| 370 |
<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%)]" />
|
| 371 |
<div className="pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-50" />
|
| 372 |
+
<span className="relative z-10">Full Text</span>
|
| 373 |
</button>
|
| 374 |
<button onClick={()=>selectManual(v.id)} className="relative overflow-hidden inline-flex items-center justify-center gap-2 px-3 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">
|
| 375 |
<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%)]" />
|