Spaces:
Running
Running
Deploy Rachana Data Studio
Browse files- frontend/app/page.tsx +6 -5
frontend/app/page.tsx
CHANGED
|
@@ -130,7 +130,7 @@ export default function Home() {
|
|
| 130 |
const [message, setMessage] = useState("");
|
| 131 |
const [authChecked, setAuthChecked] = useState(false);
|
| 132 |
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
| 133 |
-
const canAccessAdmin = user ?
|
| 134 |
|
| 135 |
const refreshOverview = useCallback(async (authToken: string) => {
|
| 136 |
const data = await api<{ queues: QueueCounts; sources: Source[]; user: User; token_progress: TokenProgress; collaboration: Collaboration }>("/api/overview", authToken);
|
|
@@ -288,15 +288,16 @@ type EditorState = { cleaned: Record<string, unknown>; tags: string; notes: stri
|
|
| 288 |
type ReviewLine = { id: string; left: string; right: string };
|
| 289 |
|
| 290 |
function ReviewWorkspace({ queue, queues, sources, sample, busy, collaboration, onQueue, onClaim, onSubmit }: { queue: string; queues: QueueCounts; sources: Source[]; sample: Sample | null; busy: boolean; collaboration: Collaboration | null; onQueue: (q: string) => void; onClaim: (datasetKey?: string) => void; onSubmit: (action: string, editor: EditorState) => void }) {
|
| 291 |
-
const [datasetKey, setDatasetKey] = useState(
|
| 292 |
useEffect(() => {
|
| 293 |
-
if (
|
| 294 |
-
setDatasetKey(
|
| 295 |
}
|
| 296 |
}, [datasetKey, sources]);
|
| 297 |
const selectedSource = sources.find((source) => source.dataset_key === datasetKey);
|
| 298 |
const selectedQueue = selectedSource?.queue_name || queue;
|
| 299 |
-
|
|
|
|
| 300 |
}
|
| 301 |
|
| 302 |
function ReviewEditor({ sample, busy, onSubmit }: { sample: Sample; busy: boolean; onSubmit: (action: string, editor: EditorState) => void }) {
|
|
|
|
| 130 |
const [message, setMessage] = useState("");
|
| 131 |
const [authChecked, setAuthChecked] = useState(false);
|
| 132 |
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
| 133 |
+
const canAccessAdmin = user ? ["admin", "owner"].includes(user.role) || user.permissions.some((p) => ["import", "export", "manage_sources", "manage_users"].includes(p)) : false;
|
| 134 |
|
| 135 |
const refreshOverview = useCallback(async (authToken: string) => {
|
| 136 |
const data = await api<{ queues: QueueCounts; sources: Source[]; user: User; token_progress: TokenProgress; collaboration: Collaboration }>("/api/overview", authToken);
|
|
|
|
| 288 |
type ReviewLine = { id: string; left: string; right: string };
|
| 289 |
|
| 290 |
function ReviewWorkspace({ queue, queues, sources, sample, busy, collaboration, onQueue, onClaim, onSubmit }: { queue: string; queues: QueueCounts; sources: Source[]; sample: Sample | null; busy: boolean; collaboration: Collaboration | null; onQueue: (q: string) => void; onClaim: (datasetKey?: string) => void; onSubmit: (action: string, editor: EditorState) => void }) {
|
| 291 |
+
const [datasetKey, setDatasetKey] = useState("");
|
| 292 |
useEffect(() => {
|
| 293 |
+
if (datasetKey && !sources.some((source) => source.dataset_key === datasetKey)) {
|
| 294 |
+
setDatasetKey("");
|
| 295 |
}
|
| 296 |
}, [datasetKey, sources]);
|
| 297 |
const selectedSource = sources.find((source) => source.dataset_key === datasetKey);
|
| 298 |
const selectedQueue = selectedSource?.queue_name || queue;
|
| 299 |
+
const claimLabel = selectedSource ? `Claim from ${selectedSource.name}` : "Claim from all active datasets";
|
| 300 |
+
return <><header className="pageHeader"><div><span className="eyebrow">Review workspace</span><h1>Make one clear decision at a time.</h1><p>Each reviewer gets a random available sample. Token progress stays global across the whole team.</p></div><div className="headerStats">{collaboration && <div className="metricInline"><span>Total cleaned</span><strong>{collaboration.total_cleaned_samples.toLocaleString()}</strong></div>}{sample?.lease && <div className="lease">Reserved until {new Date(sample.lease.expires_at).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}</div>}</div></header><section className="tableCard connectedCard"><div className="connectedHeader"><div><h2>Connected datasets</h2><p>Leave the selector empty to pool across every active dataset in this queue.</p></div><label>Active dataset<select value={datasetKey} onChange={(e) => { const next = e.target.value; setDatasetKey(next); const nextSource = sources.find((source) => source.dataset_key === next); if (nextSource) onQueue(nextSource.queue_name); }}><option value="">All active datasets</option>{sources.map((source) => <option key={source.dataset_key} value={source.dataset_key}>{source.name}</option>)}</select></label></div><div className="connectedList">{sources.map((source) => <button key={source.dataset_key} type="button" className={`connectedChip${source.dataset_key === datasetKey ? " active" : ""}`} onClick={() => { setDatasetKey(source.dataset_key); onQueue(source.queue_name); }}><strong>{source.name}</strong><small>{source.dataset_key}</small></button>)}</div></section><div className="queueTabs">{QUEUES.map((item) => <button key={item.id} className={queue === item.id ? "active" : ""} onClick={() => onQueue(item.id)}><strong>{item.label}</strong><span>{queues[item.id]?.pending || 0} pending</span></button>)}</div>{sample ? <ReviewEditor key={sample.sample_id} sample={sample} busy={busy} onSubmit={onSubmit} /> : <div className="emptyState"><div>✓</div><h2>Ready for the next item?</h2><p>{selectedSource ? `${selectedSource.name} is mapped to ${selectedQueue}.` : QUEUES.find((q) => q.id === queue)?.description}</p><button className="primary" onClick={() => onClaim(datasetKey || undefined)} disabled={busy}>{busy ? "Finding an item..." : claimLabel}</button></div>}</>;
|
| 301 |
}
|
| 302 |
|
| 303 |
function ReviewEditor({ sample, busy, onSubmit }: { sample: Sample; busy: boolean; onSubmit: (action: string, editor: EditorState) => void }) {
|