feat: Add admin experiment management pages and components
- Implemented ExperimentPage for viewing and editing individual experiments. - Created ExperimentsAdminPage for managing all experiments with a list and a modal for creating new experiments. - Developed AnnotatePage for annotating experiments with different views based on annotation tools. - Added CreateExperimentModal for creating new experiments with dataset selection. - Introduced DeleteExperimentButton for confirming and executing experiment deletions. - Built EditableExperimentHeader for editing experiment details. - Created ExperimentsList component to display a list of experiments with links to their details.
This commit is contained in:
168
src/components/EditableExperimentHeader.tsx
Normal file
168
src/components/EditableExperimentHeader.tsx
Normal file
@@ -0,0 +1,168 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { updateExperiment } from '@/app/actions/experiment';
|
||||
import { Experiment } from '@/lib/model/experiment';
|
||||
|
||||
interface EditableExperimentHeaderProps {
|
||||
experiment: Experiment;
|
||||
}
|
||||
|
||||
const ANNOTATION_TOOLS = [
|
||||
{ value: 'single-choice', label: 'Single Choice' },
|
||||
];
|
||||
|
||||
export default function EditableExperimentHeader({
|
||||
experiment,
|
||||
}: EditableExperimentHeaderProps) {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [name, setName] = useState(experiment.name);
|
||||
const [description, setDescription] = useState(experiment.description || '');
|
||||
const [annotationTool, setAnnotationTool] = useState(experiment.annotationTool || 'single-choice');
|
||||
const [published, setPublished] = useState(experiment.published || false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
async function handleSave() {
|
||||
if (!name.trim()) {
|
||||
setError('Experiment name cannot be empty');
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
await updateExperiment(experiment.id, {
|
||||
name: name.trim(),
|
||||
description: description.trim() || undefined,
|
||||
annotationTool: annotationTool,
|
||||
published: published
|
||||
});
|
||||
setIsEditing(false);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to update experiment');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
setName(experiment.name);
|
||||
setAnnotationTool(experiment.annotationTool || 'single-choice');
|
||||
setPublished(experiment.published || false);
|
||||
setDescription(experiment.description || '');
|
||||
setError(null);
|
||||
setIsEditing(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow p-6 mb-6">
|
||||
{isEditing ? (
|
||||
<div>
|
||||
<label htmlFor="name" className="block text-sm font-medium mb-2">
|
||||
Experiment Name
|
||||
</label>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 mb-4"
|
||||
disabled={loading}
|
||||
/>
|
||||
|
||||
<label htmlFor="description" className="block text-sm font-medium mb-2">
|
||||
Description
|
||||
</label>
|
||||
<textarea
|
||||
id="description"
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none mb-4"
|
||||
rows={3}
|
||||
disabled={loading}
|
||||
/>
|
||||
|
||||
<label htmlFor="annotationTool" className="block text-sm font-medium mb-2">
|
||||
Annotation Tool
|
||||
</label>
|
||||
<select
|
||||
id="annotationTool"
|
||||
value={annotationTool}
|
||||
onChange={(e) => setAnnotationTool(e.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 mb-4"
|
||||
disabled={loading}
|
||||
>
|
||||
{ANNOTATION_TOOLS.map((tool) => (
|
||||
<option key={tool.value} value={tool.value}>
|
||||
{tool.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<div className="flex items-center mb-4">
|
||||
<input
|
||||
id="published"
|
||||
type="checkbox"
|
||||
checked={published}
|
||||
onChange={(e) => setPublished(e.target.checked)}
|
||||
className="w-4 h-4 text-blue-500 rounded focus:ring-2 focus:ring-blue-500"
|
||||
disabled={loading}
|
||||
/>
|
||||
<label htmlFor="published" className="ml-2 text-sm font-medium text-gray-700">
|
||||
Published
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="mb-4 p-3 bg-red-100 border border-red-400 text-red-700 rounded text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={loading}
|
||||
className="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 disabled:bg-gray-400"
|
||||
>
|
||||
{loading ? 'Saving...' : 'Save'}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleCancel}
|
||||
disabled={loading}
|
||||
className="px-4 py-2 bg-gray-300 text-gray-700 rounded-md hover:bg-gray-400 disabled:bg-gray-200"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold mb-2">{experiment.name}</h1>
|
||||
<p className="text-lg text-gray-600 mb-4">{experiment.description}</p>
|
||||
<p className="text-sm text-gray-500 mb-4">
|
||||
ID: {experiment.id} | Dataset ID: {experiment.datasetId}
|
||||
</p>
|
||||
<p className="text-sm text-gray-500 mb-4">
|
||||
Annotation Tool: {ANNOTATION_TOOLS.find(t => t.value === (experiment.annotationTool || 'single-choice'))?.label || 'Single Choice'}
|
||||
</p>
|
||||
<p className="text-sm text-gray-500 mb-4">
|
||||
Status: {experiment.published ? 'Published' : 'Draft'}
|
||||
</p>
|
||||
<p className="text-sm text-gray-500 mb-4">
|
||||
Created: {new Date(experiment.createdAt).toLocaleDateString()} |
|
||||
Updated: {new Date(experiment.updatedAt).toLocaleDateString()}
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setIsEditing(true)}
|
||||
className="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user